From 64a854adf64aa757f88de7876a1c2e21901308c1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 13:24:54 -0400 Subject: [PATCH 001/110] it begins --- .../core/sync/create_manifest_data/index.js | 410 +++++++++--------- .../sync/create_manifest_data/index.spec.js | 62 ++- .../basic/{about.svelte => +page.svelte} | 0 .../[slug].json.ts => about/+page.svelte} | 0 .../[slug].svelte => blog.json/+server.js} | 0 .../blog/{_default.svelte => +page.svelte} | 0 .../{index.json.js => [slug].json/+server.ts} | 0 .../{index.svelte => [slug]/+page.svelte} | 0 .../{index.svelte => blog/default.svelte} | 0 .../core/sync/create_manifest_data/types.d.ts | 34 ++ packages/kit/types/internal.d.ts | 14 +- 11 files changed, 296 insertions(+), 224 deletions(-) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/{about.svelte => +page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/{blog/[slug].json.ts => about/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/{blog/[slug].svelte => blog.json/+server.js} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/{_default.svelte => +page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/{index.json.js => [slug].json/+server.ts} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/{index.svelte => [slug]/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic/{index.svelte => blog/default.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/types.d.ts diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 004b3a1f39de..601ec324df0e 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -5,47 +5,6 @@ import { get_runtime_directory } from '../../utils.js'; import { posixify } from '../../../utils/filesystem.js'; import { parse_route_id } from '../../../utils/routing.js'; -/** - * A portion of a file or directory name where the name has been split into - * static and dynamic parts - * @typedef {{ - * content: string; - * dynamic: boolean; - * rest: boolean; - * type: string | null; - * }} Part - */ - -/** - * A route, consisting of an endpoint module and/or an array of components - * (n layouts and one leaf) for successful navigations and an array of - * n error components to render if navigation fails - * @typedef {{ - * id: string; - * pattern: RegExp; - * segments: Part[][]; - * page?: { - * a: Array; - * b: Array; - * }; - * endpoint?: string; - * }} Unit - */ - -/** - * @typedef {{ - * error: string | undefined; - * layouts: Record - * }} Node - */ - -/** - * @typedef {Map} Tree - */ - -const layout_pattern = /^__layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?$/; -const dunder_pattern = /(^|\/)__(?!tests?__)/; // forbid __-prefixed files/directories except __error, __layout[-...], __test__, __tests__ - const DEFAULT = 'default'; /** @@ -61,18 +20,17 @@ export default function create_manifest_data({ fallback = `${get_runtime_directory(config.kit)}/components`, cwd = process.cwd() }) { - /** @type {import('types').RouteData[]} */ - const routes = []; + /** @type {Map} */ + const route_map = new Map(); - /** @type {Map} */ - const units = new Map(); + /** @type {Map { - const extension = valid_extensions.find((ext) => file.endsWith(ext)); + list_files(config.kit.files.routes).forEach((filepath) => { + const extension = valid_extensions.find((ext) => filepath.endsWith(ext)); if (!extension) return; - const id = file - .slice(0, -extension.length) - .replace(/(?:^|\/)index((?:@[a-zA-Z0-9_-]+)?(?:\.[a-z]+)?)?$/, '$1'); - const project_relative = `${routes_base}/${file}`; + const project_relative = `${routes_base}/${filepath}`; + const segments = filepath.split('/'); + const file = /** @type {string} */ (segments.pop()); + + if (file[0] !== '+') return; // not a route file - const segments = id.split('/'); - const name = /** @type {string} */ (segments.pop()); + const item = analyze(file, config.extensions, config.kit.moduleExtensions); - if (name === '__layout.reset') { + if (!item) { throw new Error( - '__layout.reset has been removed in favour of named layouts: https://kit.svelte.dev/docs/layouts#named-layouts' + `Files and directories prefixed with + are reserved (saw ${project_relative})` ); } - if (name === '__error' || layout_pattern.test(name)) { - const dir = segments.join('/'); + const id = segments.join('/'); - if (!tree.has(dir)) { - tree.set(dir, { - error: undefined, - layouts: {} - }); + if (/\]\[/.test(id)) { + throw new Error(`Invalid route ${project_relative} — parameters must be separated`); + } + + if (count_occurrences('[', id) !== count_occurrences(']', id)) { + throw new Error(`Invalid route ${project_relative} — brackets are unbalanced`); + } + + // error/layout files should be added to the tree, but don't result + // in a route being created, so deal with them first. note: we are + // relying on the fact that the +error and +layout files precede + // +page files alphabetically, and will therefore be processes + // before we reach the page + if (item.kind === 'component' && item.is_error) { + tree_node(id).error = project_relative; + return; + } + + if (item.is_layout) { + if (item.declares_layout === DEFAULT) { + throw new Error(`${project_relative} cannot use reserved "${DEFAULT}" name`); } - const group = /** @type {Node} */ (tree.get(dir)); + const layout_id = item.declares_layout || DEFAULT; - if (name === '__error') { - group.error = project_relative; - } else { - const match = /** @type {RegExpMatchArray} */ (layout_pattern.exec(name)); + const group = tree_node(id); - if (match[1] === DEFAULT) { - throw new Error(`${project_relative} cannot use reserved "${DEFAULT}" name`); - } + const defined = + group.layouts[layout_id] || + (group.layouts[layout_id] = { + layout: DEFAULT + }); - const layout_id = match[1] || DEFAULT; + if (defined[item.kind]) { + // edge case + throw new Error( + `Duplicate layout ${project_relative} already defined at ${defined[item.kind]}` + ); + } - const defined = group.layouts[layout_id]; - if (defined && defined !== default_layout) { - throw new Error( - `Duplicate layout ${project_relative} already defined at ${defined.file}` - ); - } + defined[item.kind] = project_relative; - group.layouts[layout_id] = { - file: project_relative, - name - }; + if (item.kind === 'component' && item.uses_layout) { + defined.layout = item.uses_layout; } return; - } else if (dunder_pattern.test(file)) { - throw new Error( - `Files and directories prefixed with __ are reserved (saw ${project_relative})` - ); } - if (!config.kit.routes(file)) return; + const type = item.kind === 'module' && !item.is_layout && !item.is_page ? 'endpoint' : 'page'; - if (/\]\[/.test(id)) { - throw new Error(`Invalid route ${project_relative} — parameters must be separated`); + if (type === 'endpoint' && route_map.has(id)) { + // note that we are relying on +server being lexically ordered after + // all other route files — if we added +view or something this is + // potentially brittle, since the server might be added before + // another route file. a problem for another day + throw new Error( + `${file} cannot share a directory with other route files (${project_relative})` + ); } - if (count_occurrences('[', id) !== count_occurrences(']', id)) { - throw new Error(`Invalid route ${project_relative} — brackets are unbalanced`); - } + if (!route_map.has(id)) { + const pattern = parse_route_id(id).pattern; - if (!units.has(id)) { - units.set(id, { + segment_map.set( id, - pattern: parse_route_id(id).pattern, - segments: id - .split('/') - .filter(Boolean) - .map((segment) => { - /** @type {Part[]} */ - const parts = []; - segment.split(/\[(.+?)\]/).map((content, i) => { - const dynamic = !!(i % 2); - - if (!content) return; - - parts.push({ - content, - dynamic, - rest: dynamic && content.startsWith('...'), - type: (dynamic && content.split('=')[1]) || null - }); + segments.filter(Boolean).map((segment) => { + /** @type {import('./types').Part[]} */ + const parts = []; + segment.split(/\[(.+?)\]/).map((content, i) => { + const dynamic = !!(i % 2); + + if (!content) return; + + parts.push({ + content, + dynamic, + rest: dynamic && content.startsWith('...'), + type: (dynamic && content.split('=')[1]) || null }); - return parts; - }), - page: undefined, - endpoint: undefined - }); + }); + return parts; + }) + ); + + if (type === 'endpoint') { + route_map.set(id, { + type, + id, + pattern, + file: project_relative + }); + } else { + route_map.set(id, { + type, + id, + pattern, + errors: [], + layouts: [], + page: {} + }); + } } - const unit = /** @type {Unit} */ (units.get(id)); + if (item.is_page) { + const route = /** @type {import('types').PageData} */ (route_map.get(id)); - if (config.extensions.find((ext) => file.endsWith(ext))) { - const { layouts, errors } = trace(project_relative, file, tree, config.extensions); - unit.page = { - a: layouts.concat(project_relative), - b: errors - }; - } else { - unit.endpoint = project_relative; + if (item.kind === 'component') { + route.page.component = project_relative; + + const { layouts, errors } = trace(tree, id, item.uses_layout, project_relative); + route.layouts = layouts; + route.errors = errors; + } else if (item.is_server) { + route.page.server = project_relative; + } else { + route.page.module = project_relative; + } } }); } @@ -205,8 +203,8 @@ export default function create_manifest_data({ tree.forEach(({ layouts, error }) => { // we do [default, error, ...other_layouts] so that components[0] and [1] // are the root layout/error. kinda janky, there's probably a nicer way - if (layouts[DEFAULT]) { - components.push(layouts[DEFAULT].file); + if (layouts[DEFAULT]?.component) { + components.push(layouts[DEFAULT].component); } if (error) { @@ -214,42 +212,19 @@ export default function create_manifest_data({ } for (const id in layouts) { - if (id !== DEFAULT) components.push(layouts[id].file); + if (id !== DEFAULT && layouts[id].component) { + components.push(layouts[id].component); + } } }); - units.forEach((unit) => { - if (unit.page) { - const leaf = /** @type {string} */ (unit.page.a[unit.page.a.length - 1]); - components.push(leaf); + route_map.forEach((route) => { + if (route.type === 'page' && route.page.component) { + components.push(route.page.component); } }); - Array.from(units.values()) - .sort(compare) - .forEach((unit) => { - // TODO when we introduce layout endpoints and scoped middlewares, we - // will probably want to have a single unified route type here - // (created in the list_files(...).forEach(...) callback) - if (unit.page) { - routes.push({ - type: 'page', - id: unit.id, - pattern: unit.pattern, - path: unit.id.includes('[') ? '' : `/${unit.id.replace(/@(?:[a-zA-Z0-9_-]+)/g, '')}`, - shadow: unit.endpoint || null, - a: unit.page.a, - b: unit.page.b - }); - } else if (unit.endpoint) { - routes.push({ - type: 'endpoint', - id: unit.id, - pattern: unit.pattern, - file: unit.endpoint - }); - } - }); + const routes = Array.from(route_map.values()).sort((a, b) => compare(a, b, segment_map)); /** @type {import('types').Asset[]} */ const assets = fs.existsSync(config.kit.files.assets) @@ -297,39 +272,77 @@ export default function create_manifest_data({ /** * @param {string} file - * @param {string} path - * @param {Tree} tree - * @param {string[]} extensions + * @param {string[]} component_extensions + * @param {string[]} module_extensions + * @returns {import('./types').RouteFile | null} */ -function trace(file, path, tree, extensions) { - /** @type {Array} */ +function analyze(file, component_extensions, module_extensions) { + const component_extension = component_extensions.find((ext) => file.endsWith(ext)); + if (component_extension) { + const name = file.slice(0, -component_extension.length); + const pattern = + /^\+(?:(page(@([a-zA-Z0-9_-]+))?)|(layout(-([a-zA-Z0-9_-]+))?(@([a-zA-Z0-9_-]+))?)|error)$/; + const match = pattern.exec(name); + if (!match) return null; + + return { + kind: 'component', + is_page: !!match[1], + is_layout: !!match[3], + is_error: !!match[6], + uses_layout: match[2] || match[5], + declares_layout: match[4] + }; + } + + const module_extension = module_extensions.find((ext) => file.endsWith(ext)); + if (module_extension) { + const name = file.slice(0, -module_extension.length); + const pattern = /^\+(server|(page(\.server)?)|(layout(-([a-zA-Z0-9_-]+))?(\.server)?))$/; + const match = pattern.exec(name); + if (!match) return null; + + return { + kind: 'module', + is_page: !!match[2], + is_layout: !!match[4], + is_server: !!(match[1] || match[3] || match[6]), + declares_layout: match[4] + }; + } + + return null; +} + +/** + * @param {import('./types').RouteTree} tree + * @param {string} id + * @param {string} layout_id + * @param {string} project_relative + */ +function trace(tree, id, layout_id = DEFAULT, project_relative) { + /** @type {Array} */ const layouts = []; /** @type {Array} */ const errors = []; - const parts = path.split('/'); - const filename = /** @type {string} */ (parts.pop()); - const extension = /** @type {string} */ (extensions.find((ext) => path.endsWith(ext))); - const base = filename.slice(0, -extension.length); + const parts = id.split('/'); - let layout_id = base.includes('@') ? base.split('@')[1] : DEFAULT; - - if (parts.findIndex((part) => part.indexOf('@') > -1) > -1) { - throw new Error(`Invalid route ${file} - named layouts are not allowed in directories`); - } + if (parts[0] !== '') parts.unshift(''); // TODO this is so that every path ends at '' (the root), but it's a little ugly - // walk up the tree, find which __layout and __error components + // walk up the tree, find which +layout and +error components // apply to this page while (true) { const node = tree.get(parts.join('/')); const layout = node?.layouts[layout_id]; - if (layout?.file && layouts.indexOf(layout.file) > -1) { - throw new Error(`Recursive layout detected: ${layout.file} -> ${layouts.join(' -> ')}`); + if (layout && layouts.indexOf(layout) > -1) { + // TODO this needs to be fixed for #5748 + throw new Error(`Recursive layout detected: ${layout.component} -> ${layouts.join(' -> ')}`); } - // any segment that has neither a __layout nor an __error can be discarded. + // any segment that has neither a +layout nor an +error can be discarded. // in other words these... // layouts: [a, , b, c] // errors: [d, , e, ] @@ -337,22 +350,23 @@ function trace(file, path, tree, extensions) { // ...can be compacted to these: // layouts: [a, b, c] // errors: [d, e, ] - if (node?.error || layout?.file) { + if (node?.error || layout) { errors.unshift(node?.error); - layouts.unshift(layout?.file); + layouts.unshift(layout); } - if (layout?.name.includes('@')) { - layout_id = layout.name.split('@')[1]; + if (layout?.layout) { + layout_id = layout.layout; } else { if (layout) layout_id = DEFAULT; - if (parts.length === 0) break; parts.pop(); + + if (parts.length === 0) break; } } if (layout_id !== DEFAULT) { - throw new Error(`${file} references missing layout "${layout_id}"`); + throw new Error(`${project_relative} references missing layout "${layout_id}"`); } // trim empty space off the end of the errors array @@ -364,14 +378,18 @@ function trace(file, path, tree, extensions) { } /** - * @param {Unit} a - * @param {Unit} b + * @param {import('types').RouteData} a + * @param {import('types').RouteData} b + * @param {Map} segment_map */ -function compare(a, b) { - const max_segments = Math.max(a.segments.length, b.segments.length); +function compare(a, b, segment_map) { + const a_segments = /** @type {import('./types').Part[][]} */ (segment_map.get(a.id)); + const b_segments = /** @type {import('./types').Part[][]} */ (segment_map.get(b.id)); + + const max_segments = Math.max(a_segments.length, b_segments.length); for (let i = 0; i < max_segments; i += 1) { - const sa = a.segments[i]; - const sb = b.segments[i]; + const sa = a_segments[i]; + const sb = b_segments[i]; // /x < /x/y, but /[...x]/y < /[...x] if (!sa) return a.id.includes('[...') ? +1 : -1; @@ -405,8 +423,8 @@ function compare(a, b) { } } - const a_is_endpoint = !a.page && a.endpoint; - const b_is_endpoint = !b.page && b.endpoint; + const a_is_endpoint = a.type === 'endpoint'; + const b_is_endpoint = b.type === 'endpoint'; if (a_is_endpoint !== b_is_endpoint) { return a_is_endpoint ? -1 : +1; @@ -435,12 +453,12 @@ function count_occurrences(needle, haystack) { function list_files(dir, path = '', files = []) { fs.readdirSync(dir) .sort((a, b) => { - // sort each directory in (__layout, __error, everything else) order + // sort each directory in (+layout, +error, everything else) order // so that we can trace layouts/errors immediately - if (a.startsWith('__layout')) { - if (!b.startsWith('__layout')) return -1; - } else if (b.startsWith('__layout')) { + if (a.startsWith('+layout')) { + if (!b.startsWith('+layout')) return -1; + } else if (b.startsWith('+layout')) { return 1; } else if (a.startsWith('__')) { if (!b.startsWith('__')) return -1; diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 03e9ed21b118..4f35d52112e5 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -26,72 +26,86 @@ const create = (dir, config = {}) => { }); }; -const default_layout = 'layout.svelte'; +const default_layout = { + component: 'layout.svelte' +}; + const default_error = 'error.svelte'; test('creates routes', () => { const { components, routes } = create('samples/basic'); - const index = 'samples/basic/index.svelte'; - const about = 'samples/basic/about.svelte'; - const blog = 'samples/basic/blog/index.svelte'; - const blog_$slug = 'samples/basic/blog/[slug].svelte'; + const index = 'samples/basic/+page.svelte'; + const about = 'samples/basic/about/+page.svelte'; + const blog = 'samples/basic/blog/+page.svelte'; + const blog_$slug = 'samples/basic/blog/[slug]/+page.svelte'; - assert.equal(components, [default_layout, default_error, about, blog_$slug, blog, index]); + assert.equal(components, [ + default_layout.component, + default_error, + index, + about, + blog, + blog_$slug + ]); assert.equal(routes, [ { type: 'page', id: '', pattern: /^\/$/, - path: '/', - shadow: null, - a: [default_layout, index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: index + } }, { type: 'endpoint', id: 'blog.json', pattern: /^\/blog\.json$/, - file: 'samples/basic/blog/index.json.js' + file: 'samples/basic/blog.json/+server.js' }, { type: 'page', id: 'about', pattern: /^\/about\/?$/, - path: '/about', - shadow: null, - a: [default_layout, about], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: about + } }, { type: 'page', id: 'blog', pattern: /^\/blog\/?$/, - path: '/blog', - shadow: null, - a: [default_layout, blog], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: blog + } }, { type: 'endpoint', id: 'blog/[slug].json', pattern: /^\/blog\/([^/]+?)\.json$/, - file: 'samples/basic/blog/[slug].json.ts' + file: 'samples/basic/blog/[slug].json/+server.ts' }, { type: 'page', id: 'blog/[slug]', pattern: /^\/blog\/([^/]+?)\/?$/, - path: '', - shadow: null, - a: [default_layout, blog_$slug], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: blog_$slug + } } ]); }); diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json.ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json.ts rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/about/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog.json/+server.js similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog.json/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/_default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/_default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json/+server.ts similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug].json/+server.ts diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/[slug]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic/blog/default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/types.d.ts b/packages/kit/src/core/sync/create_manifest_data/types.d.ts new file mode 100644 index 000000000000..0f8c2754884c --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/types.d.ts @@ -0,0 +1,34 @@ +import { PageNode } from 'types'; + +interface Part { + content: string; + dynamic: boolean; + rest: boolean; + type: string | null; +} + +interface RouteTreeNode { + error: string | undefined; + layouts: Record; +} + +export type RouteTree = Map; + +interface RouteComponent { + kind: 'component'; + is_page: boolean; + is_layout: boolean; + is_error: boolean; + uses_layout: string | undefined; + declares_layout: string | undefined; +} + +interface RouteModule { + kind: 'module'; + is_page: boolean; + is_layout: boolean; + is_server: boolean; + declares_layout: string | undefined; +} + +export type RouteFile = RouteComponent | RouteModule; diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 3e05cf415983..a395da2916b8 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -134,14 +134,20 @@ export interface NormalizedLoadOutputCache { private?: boolean; } +export interface PageNode { + component?: string; + module?: string; + server?: string; + layout?: string; +} + export interface PageData { type: 'page'; id: string; - shadow: string | null; pattern: RegExp; - path: string; - a: Array; - b: Array; + errors: Array; + layouts: Array; + page: PageNode; } export type PayloadScriptAttributes = From 073043fb8187d2c4c42fe10c9dd013795e0e9854 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 13:57:30 -0400 Subject: [PATCH 002/110] update some more test fixtures --- .../test/samples/basic-layout/{__layout.svelte => +layout.svelte} | 0 .../samples/basic-layout/{foo/__layout.svelte => +page.svelte} | 0 .../samples/basic-layout/foo/{index.svelte => +layout.svelte} | 0 .../test/samples/basic-layout/{index.svelte => foo/+page.svelte} | 0 .../duplicate-layout/{__layout-a.svelte => +layout-a.svelte} | 0 .../duplicate-layout/{__layout-a@x.svelte => +layout-a@x.svelte} | 0 .../{index@missing.svelte => +page@missing.svelte} | 0 .../{__layout-a.svelte => +layout-a.svelte} | 0 .../foo@a/{index.svelte => +page.svelte} | 0 .../{__layout-a.svelte => +layout-a.svelte} | 0 .../foo@a/{bar.svelte => bar/+page.svelte} | 0 .../{__layout-a@b.svelte => +layout-a@b.svelte} | 0 .../{__layout-b@a.svelte => +layout-b@a.svelte} | 0 .../named-layout-recursive-1/{index@a.svelte => +page@a.svelte} | 0 .../{__layout-a@a.svelte => +layout-a@a.svelte} | 0 .../named-layout-recursive-2/{index@a.svelte => +page@a.svelte} | 0 .../{__layout-a@default.svelte => +layout-a@default.svelte} | 0 .../{__layout@a.svelte => +layout@a.svelte} | 0 .../named-layout-recursive-3/{index.svelte => +page.svelte} | 0 .../{__layout-home@default.svelte => +layout-home@default.svelte} | 0 .../{__layout-special.svelte => +layout-special.svelte} | 0 .../samples/named-layouts/{__layout.svelte => +layout.svelte} | 0 .../samples/named-layouts/a/{__layout.svelte => +layout.svelte} | 0 .../test/samples/named-layouts/a/{a1.svelte => a1/+page.svelte} | 0 .../a/{a2@special.svelte => a2/+page@special.svelte} | 0 ...ospecial@special.svelte => +layout-alsospecial@special.svelte} | 0 .../samples/named-layouts/b/c/{__layout.svelte => +layout.svelte} | 0 .../b/c/{c1@alsospecial.svelte => c1/+page@alsospecial.svelte} | 0 .../named-layouts/b/c/{c2@home.svelte => c2/+page@home.svelte} | 0 ...special@special.svelte => +layout-extraspecial@special.svelte} | 0 .../b/d/{__layout-special.svelte => +layout-special.svelte} | 0 .../samples/named-layouts/b/d/{d1.svelte => +page@special.svelte} | 0 .../named-layouts/b/d/{d2@extraspecial.svelte => d1/+page.svelte} | 0 .../b/d/{index@special.svelte => d2/+page@extraspecial.svelte} | 0 34 files changed, 0 insertions(+), 0 deletions(-) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/{__layout.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/{foo/__layout.svelte => +page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/{index.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/{index.svelte => foo/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/{__layout-a.svelte => +layout-a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/{__layout-a@x.svelte => +layout-a@x.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/{index@missing.svelte => +page@missing.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/{__layout-a.svelte => +layout-a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/{index.svelte => +page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/{__layout-a.svelte => +layout-a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/{bar.svelte => bar/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/{__layout-a@b.svelte => +layout-a@b.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/{__layout-b@a.svelte => +layout-b@a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/{index@a.svelte => +page@a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/{__layout-a@a.svelte => +layout-a@a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/{index@a.svelte => +page@a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/{__layout-a@default.svelte => +layout-a@default.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/{__layout@a.svelte => +layout@a.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/{index.svelte => +page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/{__layout-home@default.svelte => +layout-home@default.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/{__layout-special.svelte => +layout-special.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/{__layout.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/{__layout.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/{a1.svelte => a1/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/{a2@special.svelte => a2/+page@special.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/{__layout-alsospecial@special.svelte => +layout-alsospecial@special.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/{__layout.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/{c1@alsospecial.svelte => c1/+page@alsospecial.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/{c2@home.svelte => c2/+page@home.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/{__layout-extraspecial@special.svelte => +layout-extraspecial@special.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/{__layout-special.svelte => +layout-special.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/{d1.svelte => +page@special.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/{d2@extraspecial.svelte => d1/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/{index@special.svelte => d2/+page@extraspecial.svelte} (100%) diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/basic-layout/foo/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a@x.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a@x.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/__layout-a@x.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/duplicate-layout/+layout-a@x.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/index@missing.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/+page@missing.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/index@missing.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-missing/+page@missing.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/__layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/+layout-a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/__layout-a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/+layout-a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/__layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/+layout-a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/__layout-a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/+layout-a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-a@b.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-a@b.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-a@b.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-a@b.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-b@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-b@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/__layout-b@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+layout-b@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/index@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+page@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/index@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-1/+page@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/__layout-a@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+layout-a@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/__layout-a@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+layout-a@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/index@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+page@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/index@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-2/+page@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout-a@default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout-a@default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout-a@default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout-a@default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout@a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout@a.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/__layout@a.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+layout@a.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-recursive-3/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-home@default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-home@default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-home@default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-home@default.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout-special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout-special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a1/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2/+page@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/a/a2/+page@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/__layout-alsospecial@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/+layout-alsospecial@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/__layout-alsospecial@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/+layout-alsospecial@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1@alsospecial.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1/+page@alsospecial.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1@alsospecial.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c1/+page@alsospecial.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2@home.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2/+page@home.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2@home.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/c/c2/+page@home.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-extraspecial@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-extraspecial@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-extraspecial@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-extraspecial@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/__layout-special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+layout-special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+page@special.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/+page@special.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2@extraspecial.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2@extraspecial.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d1/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/index@special.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2/+page@extraspecial.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/index@special.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layouts/b/d/d2/+page@extraspecial.svelte From a94516ba87cd0126b660d38362533153e17f4347 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 13:59:14 -0400 Subject: [PATCH 003/110] update more tests --- .../nested-errors/foo/{__layout.svelte => +layout.svelte} | 0 .../nested-errors/foo/bar/{__error.svelte => +error.svelte} | 0 .../nested-errors/foo/bar/baz/{__error.svelte => +error.svelte} | 0 .../foo/bar/baz/{__layout.svelte => +layout.svelte} | 0 .../nested-errors/foo/bar/baz/{index.svelte => +page.svelte} | 0 .../test/samples/rest-prefix-suffix/[...rest].json/+server.js | 2 ++ .../{[...rest].json.js => prefix-[...rest]/+page.svelte} | 0 .../test/samples/rest/a/[...rest]/+page.server.js | 2 ++ .../prefix-[...rest].svelte => rest/a/[...rest]/+page.svelte} | 0 .../test/samples/rest/b/[...rest]/+page.server.ts | 2 ++ .../samples/rest/{a/[...rest].js => b/[...rest]/+page.svelte} | 0 .../samples/{rest/a/[...rest].svelte => sorting/+page.svelte} | 0 .../[...rest].svelte => sorting/[...anotherrest]/+page.svelte} | 0 .../{rest/b/[...rest].ts => sorting/[...rest]/+page.svelte} | 0 .../index.svelte => [...rest]/abc/+page.svelte} | 0 .../samples/sorting/[...rest]/{abc.svelte => deep/+page.svelte} | 0 .../deep/[...deep_rest]/{index.svelte => +page.svelte} | 0 .../deep/[...deep_rest]/{xyz.svelte => xyz/+page.svelte} | 0 .../test/samples/sorting/[endpoint]/+server.js | 2 ++ .../{[...rest]/deep/index.svelte => [wildcard]/+page.svelte} | 0 .../sorting/{[...rest]/index.svelte => about/+page.svelte} | 0 .../test/samples/sorting/{[endpoint].js => post/+page.svelte} | 0 .../sorting/{[wildcard].svelte => post/[id]/+page.svelte} | 0 .../samples/sorting/{about.svelte => post/bar/+page.svelte} | 0 .../create_manifest_data/test/samples/sorting/post/f[xx].svelte | 0 .../samples/sorting/{index.svelte => post/f[xx]/+page.svelte} | 0 .../test/samples/sorting/post/f[yy].json.js | 0 .../test/samples/sorting/post/f[yy].json/+server.js | 2 ++ .../create_manifest_data/test/samples/sorting/post/f[yy].svelte | 0 .../samples/sorting/post/{[id].svelte => f[yy]/+page.svelte} | 0 .../create_manifest_data/test/samples/sorting/post/f[zz].ts | 0 .../test/samples/sorting/post/f[zz]/+server.ts | 2 ++ .../create_manifest_data/test/samples/sorting/post/foo.svelte | 0 .../test/samples/sorting/post/{bar.svelte => foo/+page.svelte} | 0 .../create_manifest_data/test/samples/sorting/post/index.svelte | 0 35 files changed, 12 insertions(+) rename packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/{__layout.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/{__error.svelte => +error.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/{__error.svelte => +error.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/{__layout.svelte => +layout.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/{index.svelte => +page.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json/+server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/{[...rest].json.js => prefix-[...rest]/+page.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/{rest-prefix-suffix/prefix-[...rest].svelte => rest/a/[...rest]/+page.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.server.ts rename packages/kit/src/core/sync/create_manifest_data/test/samples/rest/{a/[...rest].js => b/[...rest]/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/{rest/a/[...rest].svelte => sorting/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/{rest/b/[...rest].svelte => sorting/[...anotherrest]/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/{rest/b/[...rest].ts => sorting/[...rest]/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{[...anotherrest]/index.svelte => [...rest]/abc/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/{abc.svelte => deep/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/{index.svelte => +page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/{xyz.svelte => xyz/+page.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint]/+server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{[...rest]/deep/index.svelte => [wildcard]/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{[...rest]/index.svelte => about/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{[endpoint].js => post/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{[wildcard].svelte => post/[id]/+page.svelte} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{about.svelte => post/bar/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx].svelte rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/{index.svelte => post/f[xx]/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json.js create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json/+server.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].svelte rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/{[id].svelte => f[yy]/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz].ts create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz]/+server.ts delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo.svelte rename packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/{bar.svelte => foo/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/index.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/+error.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/__error.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/+error.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+error.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__error.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+error.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+layout.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/__layout.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+layout.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/nested-errors/foo/bar/baz/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/[...rest].json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.server.js new file mode 100644 index 000000000000..792678c3a8a6 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +page.server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292699)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest-prefix-suffix/prefix-[...rest].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.server.ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.server.ts new file mode 100644 index 000000000000..792678c3a8a6 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.server.ts @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +page.server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292699)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/a/[...rest].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/rest/b/[...rest].ts rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...anotherrest]/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/abc.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/[...deep_rest]/xyz/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint]/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint]/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint]/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/deep/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[...rest]/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[endpoint].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/[wildcard].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/about.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx].svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[xx]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].json/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy].svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id].svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy]/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/[id].svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[yy]/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz].ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz].ts deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz]/+server.ts b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz]/+server.ts new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/f[zz]/+server.ts @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/bar.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/foo/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/sorting/post/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 From db76eaaad56f13aa9c9d97e318d9812fef4e0e0c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:05:23 -0400 Subject: [PATCH 004/110] more --- .../test/samples/illegal-dunder/__foo.svelte | 0 .../test/samples/invalid-params/[foo][bar].js | 0 .../test/samples/invalid-params/[foo][bar]/+server.js | 2 ++ .../test/samples/invalid-qualifier/[foo([a-z]([0-9]))].js | 0 4 files changed, 2 insertions(+) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/illegal-dunder/__foo.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar].js create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar]/+server.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-qualifier/[foo([a-z]([0-9]))].js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/illegal-dunder/__foo.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/illegal-dunder/__foo.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar].js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar]/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar]/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-params/[foo][bar]/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-qualifier/[foo([a-z]([0-9]))].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/invalid-qualifier/[foo([a-z]([0-9]))].js deleted file mode 100644 index e69de29bb2d1..000000000000 From fe39c17d7bd3a6a133f2fb3d21dcd71ef078f075 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:05:37 -0400 Subject: [PATCH 005/110] more --- .../test/samples/encoding/{%23.svelte => %23/+page.svelte} | 0 .../sync/create_manifest_data/test/samples/lockfiles/foo.js_tmp | 0 .../create_manifest_data/test/samples/lockfiles/foo/+server.js | 2 ++ .../test/samples/lockfiles/{foo.js => foo/+server.js_tmp} | 0 4 files changed, 2 insertions(+) rename packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/{%23.svelte => %23/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js_tmp create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/{foo.js => foo/+server.js_tmp} (100%) diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/encoding/%23/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js_tmp b/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js_tmp deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js_tmp similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/lockfiles/foo/+server.js_tmp From 726afa5da0c6a2632be6deb8babcec2d87bbad93 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:06:54 -0400 Subject: [PATCH 006/110] more --- .../kit/src/core/sync/create_manifest_data/index.spec.js | 7 ------- .../samples/hidden-underscore/{e/f/g/h.js => +page.svelte} | 0 .../test/samples/hidden-underscore/e/f/g/h/+server.js | 2 ++ .../test/samples/multiple-slugs/[file].[ext]/+server.js | 2 ++ .../index.svelte => symlinks/bar/+page.svelte} | 0 .../test/samples/symlinks/bar/index.svelte | 0 .../[file].[ext].js => symlinks/routes/+page.svelte} | 0 .../test/samples/symlinks/routes/index.svelte | 0 8 files changed, 4 insertions(+), 7 deletions(-) rename packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/{e/f/g/h.js => +page.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h/+server.js create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext]/+server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/{hidden-underscore/index.svelte => symlinks/bar/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/index.svelte rename packages/kit/src/core/sync/create_manifest_data/test/samples/{multiple-slugs/[file].[ext].js => symlinks/routes/+page.svelte} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/index.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 4f35d52112e5..61318e0c2018 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -468,13 +468,6 @@ test('includes nested error components', () => { ]); }); -test('errors on encountering an illegal __file', () => { - assert.throws( - () => create('samples/illegal-dunder'), - /Files and directories prefixed with __ are reserved \(saw samples\/illegal-dunder\/__foo.svelte\)/ - ); -}); - test('creates routes with named layouts', () => { const { components, routes } = create('samples/named-layouts'); diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/e/f/g/h/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext]/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext]/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext]/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-underscore/index.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/bar/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext].js b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/multiple-slugs/[file].[ext].js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/symlinks/routes/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 From 576ebf003a24f0e23440d034c5aae5a7b0667ab1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:13:13 -0400 Subject: [PATCH 007/110] more --- .../sync/create_manifest_data/index.spec.js | 34 ------------------- .../{about.jazz => +page.funk} | 0 .../{blog/[slug].beebop => about/+page.jazz} | 0 .../custom-extension/blog.json/+server.js | 3 ++ .../blog/{[slug].json.js => +page.svelte} | 0 .../blog/[slug].json/+server.js | 2 ++ .../{index.json.js => [slug]/+page.beebop} | 0 .../custom-extension/blog/index.svelte | 0 .../test/samples/custom-extension/index.funk | 0 .../samples/hidden-by-excludes-config/.a.js | 0 .../.well-known/dnt-policy.txt.js | 0 .../hidden-by-excludes-config/__error.svelte | 0 .../hidden-by-excludes-config/__layout.svelte | 0 .../samples/hidden-by-excludes-config/_a.js | 0 .../samples/hidden-by-excludes-config/a.js | 0 .../samples/hidden-by-excludes-config/a.md | 0 .../hidden-by-excludes-config/a.spec.js | 0 .../hidden-by-excludes-config/subdir/.a.js | 0 .../subdir/.well-known/dnt-policy.txt.js | 0 .../subdir/__error.svelte | 0 .../subdir/__layout.svelte | 0 .../hidden-by-excludes-config/subdir/_a.js | 0 .../hidden-by-excludes-config/subdir/a.js | 0 .../hidden-by-excludes-config/subdir/a.md | 0 .../subdir/a.spec.js | 0 .../legal-dunder/__test__/legal.test.svelte | 0 .../legal-dunder/__tests__/legal.test.svelte | 0 27 files changed, 5 insertions(+), 34 deletions(-) rename packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/{about.jazz => +page.funk} (100%) rename packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/{blog/[slug].beebop => about/+page.jazz} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog.json/+server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/{[slug].json.js => +page.svelte} (100%) create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json/+server.js rename packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/{index.json.js => [slug]/+page.beebop} (100%) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/index.funk delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.md delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__test__/legal.test.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__tests__/legal.test.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 61318e0c2018..2ae85c6c44a6 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -292,34 +292,6 @@ test('ignores files and directories with leading dots except .well-known', () => ]); }); -test('ignores files by `kit.excludes` config w/RegExp', () => { - const { routes } = create('samples/hidden-by-excludes-config', { - kit: { - routes: (filepath) => - !filepath.endsWith('.test.js') && - !filepath.endsWith('.spec.js') && - !filepath.endsWith('.md') - } - }); - - assert.equal( - routes - .map((r) => r.type === 'endpoint' && r.file) - .filter(Boolean) - .sort(), - [ - 'samples/hidden-by-excludes-config/.a.js', - 'samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js', - 'samples/hidden-by-excludes-config/_a.js', - 'samples/hidden-by-excludes-config/a.js', - 'samples/hidden-by-excludes-config/subdir/.a.js', - 'samples/hidden-by-excludes-config/subdir/_a.js', - 'samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js', - 'samples/hidden-by-excludes-config/subdir/a.js' - ].sort() - ); -}); - test('allows multiple slugs', () => { const { routes } = create('samples/multiple-slugs'); @@ -630,12 +602,6 @@ test('errors on layout in directory', () => { ); }); -test('allows for __tests__ directories', () => { - const { routes } = create('samples/legal-dunder'); - - assert.equal(routes, []); -}); - test('creates param matchers', () => { const { matchers } = create('samples/basic'); // directory doesn't matter for the test diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about.jazz b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/+page.funk similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about.jazz rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/+page.funk diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].beebop b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about/+page.jazz similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].beebop rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/about/+page.jazz diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog.json/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog.json/+server.js new file mode 100644 index 000000000000..d2911480c512 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog.json/+server.js @@ -0,0 +1,3 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); +// @migration task: Check imports + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/+page.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug].json/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.json.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug]/+page.beebop similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.json.js rename to packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/[slug]/+page.beebop diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/blog/index.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/index.funk b/packages/kit/src/core/sync/create_manifest_data/test/samples/custom-extension/index.funk deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.a.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/.well-known/dnt-policy.txt.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__error.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/__layout.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/_a.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.md b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.md deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/a.spec.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.a.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/.well-known/dnt-policy.txt.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__error.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/__layout.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/_a.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.md deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-by-excludes-config/subdir/a.spec.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__test__/legal.test.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__test__/legal.test.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__tests__/legal.test.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/legal-dunder/__tests__/legal.test.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 From d997b813716d186b64d2abded925a4c80498a8cd Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:14:20 -0400 Subject: [PATCH 008/110] last one --- .../test/samples/hidden-dot/.well-known/dnt-policy.txt.js | 0 .../samples/hidden-dot/.well-known/dnt-policy.txt/+server.js | 2 ++ 2 files changed, 2 insertions(+) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt.js create mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt/+server.js diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt/+server.js b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt/+server.js new file mode 100644 index 000000000000..8ca7512b0a79 --- /dev/null +++ b/packages/kit/src/core/sync/create_manifest_data/test/samples/hidden-dot/.well-known/dnt-policy.txt/+server.js @@ -0,0 +1,2 @@ +throw new Error("@migration task: Update +server.js (https://github.com/sveltejs/kit/discussions/5774#discussioncomment-3292701)"); + From 1a3cbd5303215183172eb3a0bd2c376ee0054672 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:15:32 -0400 Subject: [PATCH 009/110] need to add dot: true to catch routes inside .well-known --- packages/migrate/migrations/routes/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/migrate/migrations/routes/index.js b/packages/migrate/migrations/routes/index.js index 31fd556beb75..546bc1234bd4 100644 --- a/packages/migrate/migrations/routes/index.js +++ b/packages/migrate/migrations/routes/index.js @@ -59,7 +59,9 @@ export async function migrate() { config.kit?.routes ?? ((filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath)); - const files = glob(`${routes}/**`, { filesOnly: true }).map((file) => file.replace(/\\/g, '/')); + const files = glob(`${routes}/**`, { filesOnly: true, dot: true }).map((file) => + file.replace(/\\/g, '/') + ); // validate before proceeding for (const file of files) { From 08dd7d2e8972828a7a5dd4f94f124de29a6df561 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:50:41 -0400 Subject: [PATCH 010/110] fixes, update tests --- .../core/sync/create_manifest_data/index.js | 17 +- .../sync/create_manifest_data/index.spec.js | 168 +++++++++++------- 2 files changed, 106 insertions(+), 79 deletions(-) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 601ec324df0e..3e0c9cb9bf2e 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -102,13 +102,9 @@ export default function create_manifest_data({ const group = tree_node(id); - const defined = - group.layouts[layout_id] || - (group.layouts[layout_id] = { - layout: DEFAULT - }); + const defined = group.layouts[layout_id] || (group.layouts[layout_id] = {}); - if (defined[item.kind]) { + if (defined[item.kind] && id !== '' && layout_id !== DEFAULT) { // edge case throw new Error( `Duplicate layout ${project_relative} already defined at ${defined[item.kind]}` @@ -281,7 +277,7 @@ function analyze(file, component_extensions, module_extensions) { if (component_extension) { const name = file.slice(0, -component_extension.length); const pattern = - /^\+(?:(page(@([a-zA-Z0-9_-]+))?)|(layout(-([a-zA-Z0-9_-]+))?(@([a-zA-Z0-9_-]+))?)|error)$/; + /^\+(?:(page(?:@([a-zA-Z0-9_-]+))?)|(layout(-([a-zA-Z0-9_-]+))?(@([a-zA-Z0-9_-]+))?)|error)$/; const match = pattern.exec(name); if (!match) return null; @@ -327,9 +323,7 @@ function trace(tree, id, layout_id = DEFAULT, project_relative) { /** @type {Array} */ const errors = []; - const parts = id.split('/'); - - if (parts[0] !== '') parts.unshift(''); // TODO this is so that every path ends at '' (the root), but it's a little ugly + const parts = id.split('/').filter(Boolean); // walk up the tree, find which +layout and +error components // apply to this page @@ -359,9 +353,8 @@ function trace(tree, id, layout_id = DEFAULT, project_relative) { layout_id = layout.layout; } else { if (layout) layout_id = DEFAULT; - parts.pop(); - if (parts.length === 0) break; + parts.pop(); } } diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 2ae85c6c44a6..c9b2e7f93a51 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -150,34 +150,50 @@ test_symlinks('creates symlinked routes', () => { test('creates routes with layout', () => { const { components, routes } = create('samples/basic-layout'); - const layout = 'samples/basic-layout/__layout.svelte'; - const index = 'samples/basic-layout/index.svelte'; - const foo___layout = 'samples/basic-layout/foo/__layout.svelte'; - const foo = 'samples/basic-layout/foo/index.svelte'; + const layout = 'samples/basic-layout/+layout.svelte'; + const index = 'samples/basic-layout/+page.svelte'; + const foo___layout = 'samples/basic-layout/foo/+layout.svelte'; + const foo = 'samples/basic-layout/foo/+page.svelte'; - assert.equal(components, [layout, default_error, foo___layout, foo, index]); + assert.equal(components, [layout, default_error, foo___layout, index, foo]); - assert.equal(routes, [ - { - type: 'page', - id: '', - pattern: /^\/$/, - path: '/', - shadow: null, - a: [layout, index], - b: [default_error] - }, + assert.equal( + routes.slice(1, 2), + [ + { + type: 'page', + id: '', + pattern: /^\/$/, + errors: [default_error], + layouts: [ + { + component: layout + } + ], + page: { + component: index + } + }, - { - type: 'page', - id: 'foo', - pattern: /^\/foo\/?$/, - path: '/foo', - shadow: null, - a: [layout, foo___layout, foo], - b: [default_error] - } - ]); + { + type: 'page', + id: 'foo', + pattern: /^\/foo\/?$/, + errors: [default_error], + layouts: [ + { + component: layout + }, + { + component: foo___layout + } + ], + page: { + component: foo + } + } + ].slice(1, 2) + ); }); test('succeeds when routes does not exist', () => { @@ -193,11 +209,11 @@ test('encodes invalid characters', () => { // had to remove ? and " because windows // const quote = 'samples/encoding/".svelte'; - const hash = 'samples/encoding/%23.svelte'; + const hash = 'samples/encoding/%23/+page.svelte'; // const question_mark = 'samples/encoding/?.svelte'; assert.equal(components, [ - default_layout, + default_layout.component, default_error, // quote, hash @@ -245,13 +261,30 @@ test('sorts routes correctly', () => { test('sorts routes with rest correctly', () => { const { routes } = create('samples/rest'); - assert.equal( - routes.map((p) => (p.type === 'page' ? p.a : p.file)), - [ - [default_layout, 'samples/rest/a/[...rest].svelte'], - [default_layout, 'samples/rest/b/[...rest].svelte'] - ] - ); + assert.equal(routes, [ + { + type: 'page', + id: 'a/[...rest]', + pattern: /^\/a(?:\/(.*))?\/?$/, + errors: [default_error], + layouts: [default_layout], + page: { + component: 'samples/rest/a/[...rest]/+page.svelte', + server: 'samples/rest/a/[...rest]/+page.server.js' + } + }, + { + type: 'page', + id: 'b/[...rest]', + pattern: /^\/b(?:\/(.*))?\/?$/, + errors: [default_error], + layouts: [default_layout], + page: { + component: 'samples/rest/b/[...rest]/+page.svelte', + server: 'samples/rest/b/[...rest]/+page.server.ts' + } + } + ]); }); test('allows rest parameters inside segments', () => { @@ -262,16 +295,17 @@ test('allows rest parameters inside segments', () => { type: 'page', id: 'prefix-[...rest]', pattern: /^\/prefix-(.*?)\/?$/, - path: '', - shadow: null, - a: [default_layout, 'samples/rest-prefix-suffix/prefix-[...rest].svelte'], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: 'samples/rest-prefix-suffix/prefix-[...rest]/+page.svelte' + } }, { type: 'endpoint', id: '[...rest].json', pattern: /^\/(.*?)\.json$/, - file: 'samples/rest-prefix-suffix/[...rest].json.js' + file: 'samples/rest-prefix-suffix/[...rest].json/+server.js' } ]); }); @@ -425,9 +459,9 @@ test('includes nested error components', () => { shadow: null, a: [ default_layout, - 'samples/nested-errors/foo/__layout.svelte', + 'samples/nested-errors/foo/+layout.svelte', undefined, - 'samples/nested-errors/foo/bar/baz/__layout.svelte', + 'samples/nested-errors/foo/bar/baz/+layout.svelte', 'samples/nested-errors/foo/bar/baz/index.svelte' ], b: [ @@ -444,15 +478,15 @@ test('creates routes with named layouts', () => { const { components, routes } = create('samples/named-layouts'); assert.equal(components, [ - 'samples/named-layouts/__layout.svelte', + 'samples/named-layouts/+layout.svelte', default_error, - 'samples/named-layouts/__layout-home@default.svelte', - 'samples/named-layouts/__layout-special.svelte', - 'samples/named-layouts/a/__layout.svelte', - 'samples/named-layouts/b/__layout-alsospecial@special.svelte', - 'samples/named-layouts/b/c/__layout.svelte', - 'samples/named-layouts/b/d/__layout-extraspecial@special.svelte', - 'samples/named-layouts/b/d/__layout-special.svelte', + 'samples/named-layouts/+layout-home@default.svelte', + 'samples/named-layouts/+layout-special.svelte', + 'samples/named-layouts/a/+layout.svelte', + 'samples/named-layouts/b/+layout-alsospecial@special.svelte', + 'samples/named-layouts/b/c/+layout.svelte', + 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte', + 'samples/named-layouts/b/d/+layout-special.svelte', 'samples/named-layouts/a/a1.svelte', 'samples/named-layouts/a/a2@special.svelte', 'samples/named-layouts/b/c/c1@alsospecial.svelte', @@ -470,8 +504,8 @@ test('creates routes with named layouts', () => { path: '/a/a1', shadow: null, a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/a/__layout.svelte', + 'samples/named-layouts/+layout.svelte', + 'samples/named-layouts/a/+layout.svelte', 'samples/named-layouts/a/a1.svelte' ], b: [default_error] @@ -483,7 +517,7 @@ test('creates routes with named layouts', () => { path: '/a/a2', shadow: null, a: [ - 'samples/named-layouts/__layout-special.svelte', + 'samples/named-layouts/+layout-special.svelte', 'samples/named-layouts/a/a2@special.svelte' ], b: [default_error] @@ -495,8 +529,8 @@ test('creates routes with named layouts', () => { path: '/b/d', shadow: null, a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/b/d/__layout-special.svelte', + 'samples/named-layouts/+layout.svelte', + 'samples/named-layouts/b/d/+layout-special.svelte', 'samples/named-layouts/b/d/index@special.svelte' ], b: [default_error] @@ -508,8 +542,8 @@ test('creates routes with named layouts', () => { path: '/b/c/c1', shadow: null, a: [ - 'samples/named-layouts/__layout-special.svelte', - 'samples/named-layouts/b/__layout-alsospecial@special.svelte', + 'samples/named-layouts/+layout-special.svelte', + 'samples/named-layouts/b/+layout-alsospecial@special.svelte', 'samples/named-layouts/b/c/c1@alsospecial.svelte' ], b: [default_error] @@ -521,8 +555,8 @@ test('creates routes with named layouts', () => { path: '/b/c/c2', shadow: null, a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/__layout-home@default.svelte', + 'samples/named-layouts/+layout.svelte', + 'samples/named-layouts/+layout-home@default.svelte', 'samples/named-layouts/b/c/c2@home.svelte' ], b: [default_error, default_error] @@ -533,7 +567,7 @@ test('creates routes with named layouts', () => { pattern: /^\/b\/d\/d1\/?$/, path: '/b/d/d1', shadow: null, - a: ['samples/named-layouts/__layout.svelte', 'samples/named-layouts/b/d/d1.svelte'], + a: ['samples/named-layouts/+layout.svelte', 'samples/named-layouts/b/d/d1.svelte'], b: [default_error] }, { @@ -543,9 +577,9 @@ test('creates routes with named layouts', () => { path: '/b/d/d2', shadow: null, a: [ - 'samples/named-layouts/__layout.svelte', - 'samples/named-layouts/b/d/__layout-special.svelte', - 'samples/named-layouts/b/d/__layout-extraspecial@special.svelte', + 'samples/named-layouts/+layout.svelte', + 'samples/named-layouts/b/d/+layout-special.svelte', + 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte', 'samples/named-layouts/b/d/d2@extraspecial.svelte' ], b: [default_error] @@ -563,30 +597,30 @@ test('errors on missing layout', () => { test('errors on layout named default', () => { assert.throws( () => create('samples/named-layout-default'), - /samples\/named-layout-default\/__layout-default.svelte cannot use reserved "default" name/ + /samples\/named-layout-default\/+layout-default.svelte cannot use reserved "default" name/ ); }); test('errors on duplicate layout definition', () => { assert.throws( () => create('samples/duplicate-layout'), - /Duplicate layout samples\/duplicate-layout\/__layout-a@x.svelte already defined at samples\/duplicate-layout\/__layout-a.svelte/ + /Duplicate layout samples\/duplicate-layout\/+layout-a@x.svelte already defined at samples\/duplicate-layout\/+layout-a.svelte/ ); }); test('errors on recursive name layout', () => { assert.throws( () => create('samples/named-layout-recursive-1'), - /Recursive layout detected: samples\/named-layout-recursive-1\/__layout-a@b\.svelte -> samples\/named-layout-recursive-1\/__layout-b@a\.svelte -> samples\/named-layout-recursive-1\/__layout-a@b\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-1\/+layout-a@b\.svelte -> samples\/named-layout-recursive-1\/+layout-b@a\.svelte -> samples\/named-layout-recursive-1\/+layout-a@b\.svelte/ ); assert.throws( () => create('samples/named-layout-recursive-2'), - /Recursive layout detected: samples\/named-layout-recursive-2\/__layout-a@a\.svelte -> samples\/named-layout-recursive-2\/__layout-a@a\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-2\/+layout-a@a\.svelte -> samples\/named-layout-recursive-2\/+layout-a@a\.svelte/ ); assert.throws( () => create('samples/named-layout-recursive-3'), - /Recursive layout detected: samples\/named-layout-recursive-3\/__layout@a\.svelte -> samples\/named-layout-recursive-3\/__layout-a@default\.svelte -> samples\/named-layout-recursive-3\/__layout@a\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-3\/+layout@a\.svelte -> samples\/named-layout-recursive-3\/+layout-a@default\.svelte -> samples\/named-layout-recursive-3\/+layout@a\.svelte/ ); }); From b0f714c407132be395b6517e2fe32afe7da535d9 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 14:58:45 -0400 Subject: [PATCH 011/110] fix more tests --- .../sync/create_manifest_data/index.spec.js | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index c9b2e7f93a51..04187e972cee 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -314,7 +314,7 @@ test('ignores files and directories with leading underscores', () => { const { routes } = create('samples/hidden-underscore'); assert.equal(routes.map((r) => r.type === 'endpoint' && r.file).filter(Boolean), [ - 'samples/hidden-underscore/e/f/g/h.js' + 'samples/hidden-underscore/e/f/g/h/+server.js' ]); }); @@ -322,7 +322,7 @@ test('ignores files and directories with leading dots except .well-known', () => const { routes } = create('samples/hidden-dot'); assert.equal(routes.map((r) => r.type === 'endpoint' && r.file).filter(Boolean), [ - 'samples/hidden-dot/.well-known/dnt-policy.txt.js' + 'samples/hidden-dot/.well-known/dnt-policy.txt/+server.js' ]); }); @@ -336,7 +336,7 @@ test('allows multiple slugs', () => { type: 'endpoint', id: '[file].[ext]', pattern: /^\/([^/]+?)\.([^/]+?)$/, - file: 'samples/multiple-slugs/[file].[ext].js' + file: 'samples/multiple-slugs/[file].[ext]/+server.js' } ] ); @@ -345,7 +345,7 @@ test('allows multiple slugs', () => { test('fails if dynamic params are not separated', () => { assert.throws(() => { create('samples/invalid-params'); - }, /Invalid route samples\/invalid-params\/\[foo\]\[bar\]\.js — parameters must be separated/); + }, /Invalid route samples\/invalid-params\/\[foo\]\[bar\]\/\+server\.js — parameters must be separated/); }); test('ignores things that look like lockfiles', () => { @@ -355,7 +355,7 @@ test('ignores things that look like lockfiles', () => { { type: 'endpoint', id: 'foo', - file: 'samples/lockfiles/foo.js', + file: 'samples/lockfiles/foo/+server.js', pattern: /^\/foo\/?$/ } ]); @@ -366,66 +366,77 @@ test('works with custom extensions', () => { extensions: ['.jazz', '.beebop', '.funk', '.svelte'] }); - const index = 'samples/custom-extension/index.funk'; - const about = 'samples/custom-extension/about.jazz'; - const blog = 'samples/custom-extension/blog/index.svelte'; - const blog_$slug = 'samples/custom-extension/blog/[slug].beebop'; + const index = 'samples/custom-extension/+page.funk'; + const about = 'samples/custom-extension/about/+page.jazz'; + const blog = 'samples/custom-extension/blog/+page.svelte'; + const blog_$slug = 'samples/custom-extension/blog/[slug]/+page.beebop'; - assert.equal(components, [default_layout, default_error, about, blog_$slug, blog, index]); + assert.equal(components, [ + default_layout.component, + default_error, + index, + about, + blog, + blog_$slug + ]); assert.equal(routes, [ { type: 'page', id: '', pattern: /^\/$/, - path: '/', - shadow: null, - a: [default_layout, index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: index + } }, { type: 'endpoint', id: 'blog.json', pattern: /^\/blog\.json$/, - file: 'samples/custom-extension/blog/index.json.js' + file: 'samples/custom-extension/blog.json/+server.js' }, { type: 'page', id: 'about', pattern: /^\/about\/?$/, - path: '/about', - shadow: null, - a: [default_layout, about], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: about + } }, { type: 'page', id: 'blog', pattern: /^\/blog\/?$/, - path: '/blog', - shadow: null, - a: [default_layout, blog], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: blog + } }, { type: 'endpoint', id: 'blog/[slug].json', pattern: /^\/blog\/([^/]+?)\.json$/, - file: 'samples/custom-extension/blog/[slug].json.js' + file: 'samples/custom-extension/blog/[slug].json/+server.js' }, { type: 'page', id: 'blog/[slug]', pattern: /^\/blog\/([^/]+?)\/?$/, - path: '', - shadow: null, - a: [default_layout, blog_$slug], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: { + component: blog_$slug + } } ]); }); From 22a4cbe5396548dcfbbfa71dced2cb74895d8355 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 15:45:16 -0400 Subject: [PATCH 012/110] fix various things --- .../core/sync/create_manifest_data/index.js | 14 +- .../sync/create_manifest_data/index.spec.js | 133 ++++++++---------- ...-default.svelte => +layout-default.svelte} | 0 packages/kit/types/internal.d.ts | 1 - 4 files changed, 65 insertions(+), 83 deletions(-) rename packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/{__layout-default.svelte => +layout-default.svelte} (100%) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 3e0c9cb9bf2e..57f874f359b2 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -104,7 +104,7 @@ export default function create_manifest_data({ const defined = group.layouts[layout_id] || (group.layouts[layout_id] = {}); - if (defined[item.kind] && id !== '' && layout_id !== DEFAULT) { + if (defined[item.kind] && layout_id !== DEFAULT) { // edge case throw new Error( `Duplicate layout ${project_relative} already defined at ${defined[item.kind]}` @@ -113,10 +113,6 @@ export default function create_manifest_data({ defined[item.kind] = project_relative; - if (item.kind === 'component' && item.uses_layout) { - defined.layout = item.uses_layout; - } - return; } @@ -277,7 +273,7 @@ function analyze(file, component_extensions, module_extensions) { if (component_extension) { const name = file.slice(0, -component_extension.length); const pattern = - /^\+(?:(page(?:@([a-zA-Z0-9_-]+))?)|(layout(-([a-zA-Z0-9_-]+))?(@([a-zA-Z0-9_-]+))?)|error)$/; + /^\+(?:(page(?:@([a-zA-Z0-9_-]+))?)|(layout(?:-([a-zA-Z0-9_-]+))?(?:@([a-zA-Z0-9_-]+))?)|(error))$/; const match = pattern.exec(name); if (!match) return null; @@ -349,8 +345,10 @@ function trace(tree, id, layout_id = DEFAULT, project_relative) { layouts.unshift(layout); } - if (layout?.layout) { - layout_id = layout.layout; + const parent_layout_id = layout?.component?.split('@')[1]?.split('.')[0]; + + if (parent_layout_id) { + layout_id = parent_layout_id; } else { if (layout) layout_id = DEFAULT; if (parts.length === 0) break; diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 04187e972cee..71f5bfe861db 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -466,21 +466,21 @@ test('includes nested error components', () => { type: 'page', id: 'foo/bar/baz', pattern: /^\/foo\/bar\/baz\/?$/, - path: '/foo/bar/baz', - shadow: null, - a: [ - default_layout, - 'samples/nested-errors/foo/+layout.svelte', + errors: [ + default_error, undefined, - 'samples/nested-errors/foo/bar/baz/+layout.svelte', - 'samples/nested-errors/foo/bar/baz/index.svelte' + 'samples/nested-errors/foo/bar/+error.svelte', + 'samples/nested-errors/foo/bar/baz/+error.svelte' ], - b: [ - default_error, + layouts: [ + default_layout, + { component: 'samples/nested-errors/foo/+layout.svelte' }, undefined, - 'samples/nested-errors/foo/bar/__error.svelte', - 'samples/nested-errors/foo/bar/baz/__error.svelte' - ] + { component: 'samples/nested-errors/foo/bar/baz/+layout.svelte' } + ], + page: { + component: 'samples/nested-errors/foo/bar/baz/+page.svelte' + } } ]); }); @@ -498,13 +498,13 @@ test('creates routes with named layouts', () => { 'samples/named-layouts/b/c/+layout.svelte', 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte', 'samples/named-layouts/b/d/+layout-special.svelte', - 'samples/named-layouts/a/a1.svelte', - 'samples/named-layouts/a/a2@special.svelte', - 'samples/named-layouts/b/c/c1@alsospecial.svelte', - 'samples/named-layouts/b/c/c2@home.svelte', - 'samples/named-layouts/b/d/d1.svelte', - 'samples/named-layouts/b/d/d2@extraspecial.svelte', - 'samples/named-layouts/b/d/index@special.svelte' + 'samples/named-layouts/a/a1/+page.svelte', + 'samples/named-layouts/a/a2/+page@special.svelte', + 'samples/named-layouts/b/c/c1/+page@alsospecial.svelte', + 'samples/named-layouts/b/c/c2/+page@home.svelte', + 'samples/named-layouts/b/d/+page@special.svelte', + 'samples/named-layouts/b/d/d1/+page.svelte', + 'samples/named-layouts/b/d/d2/+page@extraspecial.svelte' ]); assert.equal(routes, [ @@ -512,88 +512,73 @@ test('creates routes with named layouts', () => { type: 'page', id: 'a/a1', pattern: /^\/a\/a1\/?$/, - path: '/a/a1', - shadow: null, - a: [ - 'samples/named-layouts/+layout.svelte', - 'samples/named-layouts/a/+layout.svelte', - 'samples/named-layouts/a/a1.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/a/+layout.svelte' } ], - b: [default_error] + page: { component: 'samples/named-layouts/a/a1/+page.svelte' } }, { type: 'page', - id: 'a/a2@special', + id: 'a/a2', pattern: /^\/a\/a2\/?$/, - path: '/a/a2', - shadow: null, - a: [ - 'samples/named-layouts/+layout-special.svelte', - 'samples/named-layouts/a/a2@special.svelte' - ], - b: [default_error] + errors: [default_error], + layouts: [{ component: 'samples/named-layouts/+layout-special.svelte' }], + page: { component: 'samples/named-layouts/a/a2/+page@special.svelte' } }, { type: 'page', - id: 'b/d@special', + id: 'b/d', pattern: /^\/b\/d\/?$/, - path: '/b/d', - shadow: null, - a: [ - 'samples/named-layouts/+layout.svelte', - 'samples/named-layouts/b/d/+layout-special.svelte', - 'samples/named-layouts/b/d/index@special.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-special.svelte' } ], - b: [default_error] + page: { component: 'samples/named-layouts/b/d/+page@special.svelte' } }, { type: 'page', - id: 'b/c/c1@alsospecial', + id: 'b/c/c1', pattern: /^\/b\/c\/c1\/?$/, - path: '/b/c/c1', - shadow: null, - a: [ - 'samples/named-layouts/+layout-special.svelte', - 'samples/named-layouts/b/+layout-alsospecial@special.svelte', - 'samples/named-layouts/b/c/c1@alsospecial.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout-special.svelte' }, + { component: 'samples/named-layouts/b/+layout-alsospecial@special.svelte' } ], - b: [default_error] + page: { component: 'samples/named-layouts/b/c/c1/+page@alsospecial.svelte' } }, { type: 'page', - id: 'b/c/c2@home', + id: 'b/c/c2', pattern: /^\/b\/c\/c2\/?$/, - path: '/b/c/c2', - shadow: null, - a: [ - 'samples/named-layouts/+layout.svelte', - 'samples/named-layouts/+layout-home@default.svelte', - 'samples/named-layouts/b/c/c2@home.svelte' + errors: [default_error, default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/+layout-home@default.svelte' } ], - b: [default_error, default_error] + page: { component: 'samples/named-layouts/b/c/c2/+page@home.svelte' } }, { type: 'page', id: 'b/d/d1', pattern: /^\/b\/d\/d1\/?$/, - path: '/b/d/d1', - shadow: null, - a: ['samples/named-layouts/+layout.svelte', 'samples/named-layouts/b/d/d1.svelte'], - b: [default_error] + errors: [default_error], + layouts: [{ component: 'samples/named-layouts/+layout.svelte' }], + page: { component: 'samples/named-layouts/b/d/d1/+page.svelte' } }, { type: 'page', - id: 'b/d/d2@extraspecial', + id: 'b/d/d2', pattern: /^\/b\/d\/d2\/?$/, - path: '/b/d/d2', - shadow: null, - a: [ - 'samples/named-layouts/+layout.svelte', - 'samples/named-layouts/b/d/+layout-special.svelte', - 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte', - 'samples/named-layouts/b/d/d2@extraspecial.svelte' + errors: [default_error], + layouts: [ + { component: 'samples/named-layouts/+layout.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-special.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte' } ], - b: [default_error] + page: { component: 'samples/named-layouts/b/d/d2/+page@extraspecial.svelte' } } ]); }); @@ -601,21 +586,21 @@ test('creates routes with named layouts', () => { test('errors on missing layout', () => { assert.throws( () => create('samples/named-layout-missing'), - /samples\/named-layout-missing\/index@missing.svelte references missing layout "missing"/ + /samples\/named-layout-missing\/\+page@missing.svelte references missing layout "missing"/ ); }); test('errors on layout named default', () => { assert.throws( () => create('samples/named-layout-default'), - /samples\/named-layout-default\/+layout-default.svelte cannot use reserved "default" name/ + /samples\/named-layout-default\/\+layout-default.svelte cannot use reserved "default" name/ ); }); test('errors on duplicate layout definition', () => { assert.throws( () => create('samples/duplicate-layout'), - /Duplicate layout samples\/duplicate-layout\/+layout-a@x.svelte already defined at samples\/duplicate-layout\/+layout-a.svelte/ + /Duplicate layout samples\/duplicate-layout\/\+layout-a@x.svelte already defined at samples\/duplicate-layout\/\+layout-a.svelte/ ); }); diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/__layout-default.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/+layout-default.svelte similarity index 100% rename from packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/__layout-default.svelte rename to packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-default/+layout-default.svelte diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index a395da2916b8..636ee09ab851 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -138,7 +138,6 @@ export interface PageNode { component?: string; module?: string; server?: string; - layout?: string; } export interface PageData { From 3f3befb568a2d406fd322b7c284077aac6a3eebf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 15:51:50 -0400 Subject: [PATCH 013/110] all create_manifest_data unit tests passing --- .../core/sync/create_manifest_data/index.js | 6 ++++- .../sync/create_manifest_data/index.spec.js | 22 +++++-------------- .../+layout-a.svelte | 0 .../foo@a/+page.svelte | 0 .../+layout-a.svelte | 0 .../foo@a/bar/+page.svelte | 0 6 files changed, 11 insertions(+), 17 deletions(-) delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/+layout-a.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/+page.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/+layout-a.svelte delete mode 100644 packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar/+page.svelte diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 57f874f359b2..4d4a7784c86a 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -329,7 +329,11 @@ function trace(tree, id, layout_id = DEFAULT, project_relative) { if (layout && layouts.indexOf(layout) > -1) { // TODO this needs to be fixed for #5748 - throw new Error(`Recursive layout detected: ${layout.component} -> ${layouts.join(' -> ')}`); + throw new Error( + `Recursive layout detected: ${layout.component} -> ${layouts + .map((l) => l?.component) + .join(' -> ')}` + ); } // any segment that has neither a +layout nor an +error can be discarded. diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index 71f5bfe861db..d3d633b1bdb7 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -607,28 +607,16 @@ test('errors on duplicate layout definition', () => { test('errors on recursive name layout', () => { assert.throws( () => create('samples/named-layout-recursive-1'), - /Recursive layout detected: samples\/named-layout-recursive-1\/+layout-a@b\.svelte -> samples\/named-layout-recursive-1\/+layout-b@a\.svelte -> samples\/named-layout-recursive-1\/+layout-a@b\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-1\/\+layout-a@b\.svelte -> samples\/named-layout-recursive-1\/\+layout-b@a\.svelte -> samples\/named-layout-recursive-1\/\+layout-a@b\.svelte/ ); assert.throws( () => create('samples/named-layout-recursive-2'), - /Recursive layout detected: samples\/named-layout-recursive-2\/+layout-a@a\.svelte -> samples\/named-layout-recursive-2\/+layout-a@a\.svelte/ + /Recursive layout detected: samples\/named-layout-recursive-2\/\+layout-a@a\.svelte -> samples\/named-layout-recursive-2\/\+layout-a@a\.svelte/ ); assert.throws( () => create('samples/named-layout-recursive-3'), - /Recursive layout detected: samples\/named-layout-recursive-3\/+layout@a\.svelte -> samples\/named-layout-recursive-3\/+layout-a@default\.svelte -> samples\/named-layout-recursive-3\/+layout@a\.svelte/ - ); -}); - -test('errors on layout in directory', () => { - assert.throws( - () => create('samples/named-layout-on-directory-1'), - /Invalid route samples\/named-layout-on-directory-1\/foo@a\/index.svelte - named layouts are not allowed in directories/ - ); - - assert.throws( - () => create('samples/named-layout-on-directory-2'), - /Invalid route samples\/named-layout-on-directory-2\/foo@a\/bar.svelte - named layouts are not allowed in directories/ + /Recursive layout detected: samples\/named-layout-recursive-3\/\+layout@a\.svelte -> samples\/named-layout-recursive-3\/\+layout-a@default\.svelte -> samples\/named-layout-recursive-3\/\+layout@a\.svelte/ ); }); @@ -657,7 +645,9 @@ test('errors on duplicate matchers', () => { try { assert.throws(() => { create('samples/basic', { - extensions: ['.js', '.ts'] + kit: { + moduleExtensions: ['.js', '.ts'] + } }); }, /Duplicate matchers/); } finally { diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/+layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/+layout-a.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/+page.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-1/foo@a/+page.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/+layout-a.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/+layout-a.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar/+page.svelte b/packages/kit/src/core/sync/create_manifest_data/test/samples/named-layout-on-directory-2/foo@a/bar/+page.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 From 93ecbb9849b2b7b4a4b83497504c9b8c0ef256a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:04:40 -0400 Subject: [PATCH 014/110] update write_manifest --- .../core/sync/create_manifest_data/index.js | 21 +++++--- packages/kit/src/core/sync/write_manifest.js | 53 ++++++++++++++----- packages/kit/src/core/sync/write_root.js | 2 +- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index 4d4a7784c86a..ad4c273e9455 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -192,11 +192,16 @@ export default function create_manifest_data({ /** @type {string[]} */ const components = []; + /** @type {string[]} */ + const modules = []; + tree.forEach(({ layouts, error }) => { // we do [default, error, ...other_layouts] so that components[0] and [1] // are the root layout/error. kinda janky, there's probably a nicer way - if (layouts[DEFAULT]?.component) { - components.push(layouts[DEFAULT].component); + if (layouts[DEFAULT]) { + const { component, module } = layouts[DEFAULT]; + if (component) components.push(component); + if (module) modules.push(module); } if (error) { @@ -204,15 +209,18 @@ export default function create_manifest_data({ } for (const id in layouts) { - if (id !== DEFAULT && layouts[id].component) { - components.push(layouts[id].component); + if (id !== DEFAULT) { + const { component, module } = layouts[id]; + if (component) components.push(component); + if (module) modules.push(module); } } }); route_map.forEach((route) => { - if (route.type === 'page' && route.page.component) { - components.push(route.page.component); + if (route.type === 'page') { + if (route.page.component) components.push(route.page.component); + if (route.page.module) modules.push(route.page.module); } }); @@ -257,6 +265,7 @@ export default function create_manifest_data({ return { assets, components, + modules, routes, matchers }; diff --git a/packages/kit/src/core/sync/write_manifest.js b/packages/kit/src/core/sync/write_manifest.js index f76eac2bbaff..6d78b8d3f53a 100644 --- a/packages/kit/src/core/sync/write_manifest.js +++ b/packages/kit/src/core/sync/write_manifest.js @@ -13,31 +13,54 @@ export function write_manifest(manifest_data, base, output) { /** @type {Record} */ const component_indexes = {}; + /** @type {Record} */ + const module_indexes = {}; + /** @param {string} c */ const get_path = (c) => path.relative(base, c); - const components = `[ - ${manifest_data.components - .map((component, i) => { - component_indexes[component] = i; + const components = `[\n\t${manifest_data.components + .map((component, i) => { + component_indexes[component] = i; + return `() => import(${s(get_path(component))})`; + }) + .join(',\n\t')}\n]`; - return `() => import(${s(get_path(component))})`; - }) - .join(',\n\t\t\t\t\t')} - ]`.replace(/^\t/gm, ''); + const modules = `[\n\t${manifest_data.modules + .map((module, i) => { + module_indexes[module] = i; + return `() => import(${s(get_path(module))})`; + }) + .join(',\n\t')}\n]`; + + /** + * Serialize a page node as a pair of indices into the + * `components` and `modules` arrays + * @param {import('types').PageNode | undefined} node + */ + function serialize_node(node) { + if (!node) return ','; - /** @param {Array} parts */ - const get_indices = (parts) => - `[${parts.map((part) => (part ? component_indexes[part] : '')).join(', ')}]`; + const indices = [node.component ? component_indexes[node.component] : '']; + if (node.module) indices.push(module_indexes[node.module]); + return `[${indices.join(',')}]`; + } const dictionary = `{ ${manifest_data.routes .map((route) => { if (route.type === 'page') { - const tuple = [get_indices(route.a), get_indices(route.b)]; - if (route.shadow) tuple.push('1'); + const errors = `[${route.errors + .map((file) => file && component_indexes[file]) + .join(', ')}]`; - return `${s(route.id)}: [${tuple.join(', ')}]`; + const layouts = `[${route.layouts.map(serialize_node).join(', ')}]`; + + const page = serialize_node(route.page); + + const value = [errors, layouts, page].join(','); + + return `${s(route.id)}: [${value}]`; } }) .filter(Boolean) @@ -51,6 +74,8 @@ export function write_manifest(manifest_data, base, output) { export const components = ${components}; + export const modules = ${modules}; + export const dictionary = ${dictionary}; `) ); diff --git a/packages/kit/src/core/sync/write_root.js b/packages/kit/src/core/sync/write_root.js index 9aa745236ffb..96e98a0e5c65 100644 --- a/packages/kit/src/core/sync/write_root.js +++ b/packages/kit/src/core/sync/write_root.js @@ -9,7 +9,7 @@ export function write_root(manifest_data, output) { const max_depth = Math.max( ...manifest_data.routes.map((route) => - route.type === 'page' ? route.a.filter(Boolean).length : 0 + route.type === 'page' ? route.layouts.filter(Boolean).length + 1 : 0 ), 1 ); From 9a4beaf56415b00d6a6f2dd02628dbbd280ff2c8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:10:07 -0400 Subject: [PATCH 015/110] fix --- .../amp/src/routes/styles/{Unused/+page.svelte => Unused.svelte} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/kit/test/apps/amp/src/routes/styles/{Unused/+page.svelte => Unused.svelte} (100%) diff --git a/packages/kit/test/apps/amp/src/routes/styles/Unused/+page.svelte b/packages/kit/test/apps/amp/src/routes/styles/Unused.svelte similarity index 100% rename from packages/kit/test/apps/amp/src/routes/styles/Unused/+page.svelte rename to packages/kit/test/apps/amp/src/routes/styles/Unused.svelte From a4d8d049d8732b5a2159462b0f4afb06e254ae51 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:10:18 -0400 Subject: [PATCH 016/110] remove config.kit.routes --- packages/kit/src/core/config/index.spec.js | 1 + packages/kit/src/core/config/options.js | 6 +++++- packages/kit/types/index.d.ts | 1 - 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/core/config/index.spec.js b/packages/kit/src/core/config/index.spec.js index 1467c74bb779..4d31adcf9a18 100644 --- a/packages/kit/src/core/config/index.spec.js +++ b/packages/kit/src/core/config/index.spec.js @@ -120,6 +120,7 @@ const get_defaults = (prefix = '') => ({ }, protocol: undefined, router: undefined, + routes: undefined, ssr: undefined, target: undefined, trailingSlash: 'never', diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js index c83d3e9b009c..7abba322254a 100644 --- a/packages/kit/src/core/config/options.js +++ b/packages/kit/src/core/config/options.js @@ -291,7 +291,11 @@ const options = object( // TODO remove for 1.0 router: error((keypath) => `${keypath} has been moved to config.kit.browser.router`), - routes: fun((filepath) => !/(?:(?:^_|\/_)|(?:^\.|\/\.)(?!well-known))/.test(filepath)), + // TODO remove for 1.0 + routes: error( + (keypath) => + `${keypath} has been removed. See https://github.com/sveltejs/kit/discussions/5774 for details` + ), serviceWorker: object({ register: boolean(true), diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 02f2d277799c..510b200de6b7 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -140,7 +140,6 @@ export interface KitConfig { onError?: PrerenderOnErrorValue; origin?: string; }; - routes?: (filepath: string) => boolean; serviceWorker?: { register?: boolean; files?: (filepath: string) => boolean; From d1a6c2b13d4da93e7eae0b30ff55901d93819f81 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:15:14 -0400 Subject: [PATCH 017/110] update RequestHandler type --- packages/kit/types/index.d.ts | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 510b200de6b7..c345207b39ce 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -242,23 +242,12 @@ export interface RequestEvent = Record RequestHandlerOutput` function exported from an endpoint that corresponds to an HTTP verb (`GET`, `PUT`, `PATCH`, etc) and handles requests with that method. + * A `(event: RequestEvent) => Response` function exported from a +server.js file that corresponds to an HTTP verb (`GET`, `PUT`, `PATCH`, etc) and handles requests with that method. * * It receives `Params` as the first generic argument, which you can skip by using [generated types](/docs/types#generated-types) instead. - * - * The next generic argument `Output` is used to validate the returned `body` from your functions by passing it through `BodyValidator`, which will make sure the variable in the `body` matches what with you assign here. It defaults to `ResponseBody`, which will error when `body` receives a [custom object type](https://www.typescriptlang.org/docs/handbook/2/objects.html). */ -export interface RequestHandler< - Params extends Record = Record, - Output = ResponseBody -> { - (event: RequestEvent): MaybePromise>; -} - -export interface RequestHandlerOutput { - status?: number; - headers?: Headers | Partial; - body?: Output extends ResponseBody ? Output : BodyValidator; +export interface RequestHandler = Record> { + (event: RequestEvent): MaybePromise; } export interface ResolveOptions { From 886163b5f1c9f46e233568d83dd331b3caac4e09 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:23:31 -0400 Subject: [PATCH 018/110] simplify endpoint code --- packages/kit/src/runtime/server/endpoint.js | 101 ++---------------- .../kit/src/runtime/server/endpoint.spec.js | 24 ----- packages/kit/src/utils/http.js | 22 ---- packages/kit/src/utils/http.spec.js | 26 +---- 4 files changed, 8 insertions(+), 165 deletions(-) delete mode 100644 packages/kit/src/runtime/server/endpoint.spec.js diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 89ef59c297e6..4c89468e950c 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -1,48 +1,11 @@ -import { to_headers } from '../../utils/http.js'; -import { hash } from '../hash.js'; -import { check_method_names, is_pojo, serialize_error } from './utils.js'; - -/** @param {string} body */ -function error(body) { - return new Response(body, { - status: 500 - }); -} - -/** @param {unknown} s */ -function is_string(s) { - return typeof s === 'string' || s instanceof String; -} - -const text_types = new Set([ - 'application/xml', - 'application/json', - 'application/x-www-form-urlencoded', - 'multipart/form-data' -]); - -const bodyless_status_codes = new Set([101, 204, 205, 304]); - -/** - * Decides how the body should be parsed based on its mime type - * - * @param {string | undefined | null} content_type The `content-type` header of a request/response. - * @returns {boolean} - */ -export function is_text(content_type) { - if (!content_type) return true; // defaults to json - const type = content_type.split(';')[0].toLowerCase(); // get the mime type - - return type.startsWith('text/') || type.endsWith('+xml') || text_types.has(type); -} +import { check_method_names } from './utils.js'; /** * @param {import('types').RequestEvent} event * @param {{ [method: string]: import('types').RequestHandler }} mod - * @param {import('types').SSROptions} options * @returns {Promise} */ -export async function render_endpoint(event, mod, options) { +export async function render_endpoint(event, mod) { const { method } = event.request; check_method_names(mod); @@ -80,63 +43,13 @@ export async function render_endpoint(event, mod, options) { } const response = await handler(event); - const preface = `Invalid response from route ${event.url.pathname}`; - - if (typeof response !== 'object') { - return error(`${preface}: expected an object, got ${typeof response}`); - } - // TODO remove for 1.0 - // @ts-expect-error - if (response.fallthrough) { - throw new Error( - 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching' + if (!(response instanceof Response)) { + return new Response( + `Invalid response from route ${event.url.pathname}: handler should return a Response object`, + { status: 500 } ); } - const { status = 200, body = {} } = response; - const headers = - response.headers instanceof Headers - ? new Headers(response.headers) - : to_headers(response.headers); - - const type = headers.get('content-type'); - - if ( - !is_text(type) && - !(body instanceof Uint8Array || body instanceof ReadableStream || is_string(body)) - ) { - return error( - `${preface}: body must be an instance of string, Uint8Array or ReadableStream if content-type is not a supported textual content-type` - ); - } - - /** @type {import('types').StrictBody} */ - let normalized_body; - - if (is_pojo(body) && (!type || type.startsWith('application/json'))) { - headers.set('content-type', 'application/json; charset=utf-8'); - normalized_body = - body instanceof Error ? serialize_error(body, options.get_stack) : JSON.stringify(body); - } else { - normalized_body = /** @type {import('types').StrictBody} */ (body); - } - - if ( - (typeof normalized_body === 'string' || normalized_body instanceof Uint8Array) && - !headers.has('etag') - ) { - const cache_control = headers.get('cache-control'); - if (!cache_control || !/(no-store|immutable)/.test(cache_control)) { - headers.set('etag', `"${hash(normalized_body)}"`); - } - } - - return new Response( - method !== 'HEAD' && !bodyless_status_codes.has(status) ? normalized_body : undefined, - { - status, - headers - } - ); + return response; } diff --git a/packages/kit/src/runtime/server/endpoint.spec.js b/packages/kit/src/runtime/server/endpoint.spec.js deleted file mode 100644 index 52a9a7e491b9..000000000000 --- a/packages/kit/src/runtime/server/endpoint.spec.js +++ /dev/null @@ -1,24 +0,0 @@ -import { test } from 'uvu'; -import * as assert from 'uvu/assert'; -import { is_text } from './endpoint.js'; - -test('is_text', () => { - assert.equal(is_text(undefined), true); - assert.equal(is_text(null), true); - assert.equal(is_text(''), true); - assert.equal(is_text('TEXT/PLAIN'), true); - assert.equal(is_text('text/html'), true); - assert.equal(is_text('text/javascript'), true); - assert.equal(is_text('application/xml'), true); - assert.equal(is_text('image/svg+xml'), true); - assert.equal(is_text('application/json'), true); - assert.equal(is_text('text/plain; charset="us-ascii"'), true); - assert.equal(is_text('multipart/form-data; boundary=aBoundaryString'), true); - - assert.equal(is_text('multipart/byteranges; boundary=3d6b6a416f9b5'), false); - assert.equal(is_text('image/apng'), false); - assert.equal(is_text('IMAGE/webp'), false); - assert.equal(is_text('application/octet-stream'), false); -}); - -test.run(); diff --git a/packages/kit/src/utils/http.js b/packages/kit/src/utils/http.js index 7eea6cd42054..434a0f1f869f 100644 --- a/packages/kit/src/utils/http.js +++ b/packages/kit/src/utils/http.js @@ -1,25 +1,3 @@ -/** @param {Partial | undefined} object */ -export function to_headers(object) { - const headers = new Headers(); - - if (object) { - for (const key in object) { - const value = object[key]; - if (!value) continue; - - if (Array.isArray(value)) { - value.forEach((value) => { - headers.append(key, /** @type {string} */ (value)); - }); - } else { - headers.set(key, /** @type {string} */ (value)); - } - } - } - - return headers; -} - /** * Given an Accept header and a list of possible content types, pick * the most suitable one to respond with diff --git a/packages/kit/src/utils/http.spec.js b/packages/kit/src/utils/http.spec.js index 7e1abb277249..38e968f82f6d 100644 --- a/packages/kit/src/utils/http.spec.js +++ b/packages/kit/src/utils/http.spec.js @@ -1,30 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { negotiate, to_headers } from './http.js'; -import { Headers } from 'undici'; - -// @ts-ignore -globalThis.Headers = Headers; - -test('handle header string value', () => { - const headers = to_headers({ name: 'value' }); - assert.equal(headers.get('name'), 'value'); -}); - -test('handle header array values', () => { - const headers = to_headers({ name: ['value1', 'value2'] }); - assert.equal(headers.get('name'), 'value1, value2'); -}); - -test('handle header int value', () => { - const headers = to_headers({ name: 123 }); - assert.equal(headers.get('name'), '123'); -}); - -test('handle header decimal value', () => { - const headers = to_headers({ name: 123.456 }); - assert.equal(headers.get('name'), '123.456'); -}); +import { negotiate } from './http.js'; test('handle valid accept header value', () => { const accept = 'text/html'; From e23610d1dae79c37ffb67417f2c075635030699d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:29:13 -0400 Subject: [PATCH 019/110] simplify some more stuff --- packages/kit/src/runtime/server/endpoint.js | 7 +++++-- packages/kit/src/runtime/server/index.js | 6 +++--- packages/kit/src/runtime/server/page/index.js | 3 +-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 4c89468e950c..415d9d9c3975 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -2,12 +2,15 @@ import { check_method_names } from './utils.js'; /** * @param {import('types').RequestEvent} event - * @param {{ [method: string]: import('types').RequestHandler }} mod + * @param {import('types').SSREndpoint} route * @returns {Promise} */ -export async function render_endpoint(event, mod) { +export async function render_endpoint(event, route) { const { method } = event.request; + const mod = await route.load(); + + // TODO: Remove for 1.0 check_method_names(mod); /** @type {import('types').RequestHandler} */ diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 04406081ea58..eb380947c65a 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -217,8 +217,8 @@ export async function respond(request, options, state) { /** @type {Response} */ let response; - if (is_data_request && route.type === 'page' && route.shadow) { - response = await render_endpoint(event, await route.shadow(), options); + if (is_data_request && route.type === 'page') { + throw new Error('TODO return JSON'); // loading data for a client-side transition is a special case if (request.headers.has('x-sveltekit-load')) { @@ -240,7 +240,7 @@ export async function respond(request, options, state) { } else { response = route.type === 'endpoint' - ? await render_endpoint(event, await route.load(), options) + ? await render_endpoint(event, route) : await render_page(event, route, options, state, resolve_opts); } diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index c56e97b05270..c5584fc37682 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -1,5 +1,4 @@ import { negotiate } from '../../../utils/http.js'; -import { render_endpoint } from '../endpoint.js'; import { respond } from './respond.js'; /** @@ -25,7 +24,7 @@ export async function render_page(event, route, options, state, resolve_opts) { ]); if (type === 'application/json') { - return render_endpoint(event, await route.shadow(), options); + throw new Error('TODO return JSON'); } } From 80d4e6acbc15e631f4158d6c2493204843115cd3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:38:54 -0400 Subject: [PATCH 020/110] move respond code into render_page, where it belongs --- packages/kit/src/runtime/server/page/index.js | 299 +++++++++++++++++- .../kit/src/runtime/server/page/respond.js | 275 ---------------- 2 files changed, 282 insertions(+), 292 deletions(-) delete mode 100644 packages/kit/src/runtime/server/page/respond.js diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index c5584fc37682..5b1b47816478 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -1,5 +1,15 @@ import { negotiate } from '../../../utils/http.js'; -import { respond } from './respond.js'; +import { render_response } from './render.js'; +import { load_node } from './load_node.js'; +import { respond_with_error } from './respond_with_error.js'; +import { coalesce_to_error } from '../../../utils/error.js'; + +/** + * @typedef {import('./types.js').Loaded} Loaded + * @typedef {import('types').SSRNode} SSRNode + * @typedef {import('types').SSROptions} SSROptions + * @typedef {import('types').SSRState} SSRState + */ /** * @param {import('types').RequestEvent} event @@ -17,25 +27,280 @@ export async function render_page(event, route, options, state, resolve_opts) { }); } - if (route.shadow) { - const type = negotiate(event.request.headers.get('accept') || 'text/html', [ - 'text/html', - 'application/json' - ]); + const accept = negotiate(event.request.headers.get('accept') || 'text/html', [ + 'text/html', + 'application/json' + ]); - if (type === 'application/json') { - throw new Error('TODO return JSON'); - } + if (accept === 'application/json') { + throw new Error('TODO return JSON'); } const $session = await options.hooks.getSession(event); - return respond({ - event, - options, - state, - $session, - resolve_opts, - route - }); + /** @type {Array} */ + let nodes; + + if (!resolve_opts.ssr) { + return await render_response({ + branch: [], + page_config: { + hydrate: true, + router: true + }, + status: 200, + error: null, + event, + stuff: {}, + options, + state, + $session, + resolve_opts + }); + } + + try { + nodes = await Promise.all( + // we use == here rather than === because [undefined] serializes as "[null]" + route.a.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) + ); + } catch (err) { + const error = coalesce_to_error(err); + + options.handle_error(error, event); + + return await respond_with_error({ + event, + options, + state, + $session, + status: 500, + error, + resolve_opts + }); + } + + // the leaf node will be present. only layouts may be undefined + const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module; + + let page_config = get_page_config(leaf, options); + + if (state.prerendering) { + // if the page isn't marked as prerenderable (or is explicitly + // marked NOT prerenderable, if `prerender.default` is `true`), + // then bail out at this point + const should_prerender = leaf.prerender ?? options.prerender.default; + if (!should_prerender) { + return new Response(undefined, { + status: 204 + }); + } + } + + /** @type {Array} */ + let branch = []; + + /** @type {number} */ + let status = 200; + + /** @type {Error | null} */ + let error = null; + + /** @type {string[]} */ + let set_cookie_headers = []; + + let stuff = {}; + + ssr: { + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; + + /** @type {Loaded | undefined} */ + let loaded; + + if (node) { + try { + loaded = await load_node({ + event, + options, + state, + route, + $session, + node, + stuff, + is_error: false, + is_leaf: i === nodes.length - 1 + }); + + set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers); + + if (loaded.loaded.redirect) { + return with_cookies( + new Response(undefined, { + status: loaded.loaded.status, + headers: { + location: loaded.loaded.redirect + } + }), + set_cookie_headers + ); + } + + if (loaded.loaded.error) { + error = loaded.loaded.error; + status = loaded.loaded.status ?? 500; + } + } catch (err) { + const e = coalesce_to_error(err); + + options.handle_error(e, event); + + status = 500; + error = e; + } + + if (loaded && !error) { + branch.push(loaded); + } + + if (error) { + while (i--) { + if (route.b[i]) { + const index = /** @type {number} */ (route.b[i]); + const error_node = await options.manifest._.nodes[index](); + + /** @type {Loaded} */ + let node_loaded; + let j = i; + while (!(node_loaded = branch[j])) { + j -= 1; + } + + try { + const error_loaded = /** @type {import('./types').Loaded} */ ( + await load_node({ + event, + options, + state, + route, + $session, + node: error_node, + stuff: node_loaded.stuff, + is_error: true, + is_leaf: false, + status, + error + }) + ); + + if (error_loaded.loaded.error) { + continue; + } + + page_config = get_page_config(error_node.module, options); + branch = branch.slice(0, j + 1).concat(error_loaded); + stuff = { ...node_loaded.stuff, ...error_loaded.stuff }; + break ssr; + } catch (err) { + const e = coalesce_to_error(err); + + options.handle_error(e, event); + + continue; + } + } + } + + // TODO backtrack until we find an __error.svelte component + // that we can use as the leaf node + // for now just return regular error page + return with_cookies( + await respond_with_error({ + event, + options, + state, + $session, + status, + error, + resolve_opts + }), + set_cookie_headers + ); + } + } + + if (loaded && loaded.loaded.stuff) { + stuff = { + ...stuff, + ...loaded.loaded.stuff + }; + } + } + } + + try { + return with_cookies( + await render_response({ + event, + options, + state, + $session, + resolve_opts, + stuff, + page_config, + status, + error, + branch: branch.filter(Boolean) + }), + set_cookie_headers + ); + } catch (err) { + const error = coalesce_to_error(err); + + options.handle_error(error, event); + + return with_cookies( + await respond_with_error({ + event, + options, + state, + $session, + resolve_opts, + status: 500, + error + }), + set_cookie_headers + ); + } +} + +/** + * @param {import('types').SSRComponent} leaf + * @param {SSROptions} options + */ +function get_page_config(leaf, options) { + // TODO remove for 1.0 + if ('ssr' in leaf) { + throw new Error( + '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle' + ); + } + + return { + router: 'router' in leaf ? !!leaf.router : options.router, + hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate + }; +} + +/** + * @param {Response} response + * @param {string[]} set_cookie_headers + */ +function with_cookies(response, set_cookie_headers) { + if (set_cookie_headers.length) { + set_cookie_headers.forEach((value) => { + response.headers.append('set-cookie', value); + }); + } + return response; } diff --git a/packages/kit/src/runtime/server/page/respond.js b/packages/kit/src/runtime/server/page/respond.js deleted file mode 100644 index 961a6b1680be..000000000000 --- a/packages/kit/src/runtime/server/page/respond.js +++ /dev/null @@ -1,275 +0,0 @@ -import { render_response } from './render.js'; -import { load_node } from './load_node.js'; -import { respond_with_error } from './respond_with_error.js'; -import { coalesce_to_error } from '../../../utils/error.js'; - -/** - * @typedef {import('./types.js').Loaded} Loaded - * @typedef {import('types').SSRNode} SSRNode - * @typedef {import('types').SSROptions} SSROptions - * @typedef {import('types').SSRState} SSRState - */ - -/** - * Gets the nodes, calls `load` for each of them, and then calls render to build the HTML response. - * @param {{ - * event: import('types').RequestEvent; - * options: SSROptions; - * state: SSRState; - * $session: any; - * resolve_opts: import('types').RequiredResolveOptions; - * route: import('types').SSRPage; - * }} opts - * @returns {Promise} - */ -export async function respond(opts) { - const { event, options, state, $session, route, resolve_opts } = opts; - - /** @type {Array} */ - let nodes; - - if (!resolve_opts.ssr) { - return await render_response({ - ...opts, - branch: [], - page_config: { - hydrate: true, - router: true - }, - status: 200, - error: null, - event, - stuff: {} - }); - } - - try { - nodes = await Promise.all( - // we use == here rather than === because [undefined] serializes as "[null]" - route.a.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) - ); - } catch (err) { - const error = coalesce_to_error(err); - - options.handle_error(error, event); - - return await respond_with_error({ - event, - options, - state, - $session, - status: 500, - error, - resolve_opts - }); - } - - // the leaf node will be present. only layouts may be undefined - const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module; - - let page_config = get_page_config(leaf, options); - - if (state.prerendering) { - // if the page isn't marked as prerenderable (or is explicitly - // marked NOT prerenderable, if `prerender.default` is `true`), - // then bail out at this point - const should_prerender = leaf.prerender ?? options.prerender.default; - if (!should_prerender) { - return new Response(undefined, { - status: 204 - }); - } - } - - /** @type {Array} */ - let branch = []; - - /** @type {number} */ - let status = 200; - - /** @type {Error | null} */ - let error = null; - - /** @type {string[]} */ - let set_cookie_headers = []; - - let stuff = {}; - - ssr: { - for (let i = 0; i < nodes.length; i += 1) { - const node = nodes[i]; - - /** @type {Loaded | undefined} */ - let loaded; - - if (node) { - try { - loaded = await load_node({ - ...opts, - node, - stuff, - is_error: false, - is_leaf: i === nodes.length - 1 - }); - - set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers); - - if (loaded.loaded.redirect) { - return with_cookies( - new Response(undefined, { - status: loaded.loaded.status, - headers: { - location: loaded.loaded.redirect - } - }), - set_cookie_headers - ); - } - - if (loaded.loaded.error) { - error = loaded.loaded.error; - status = loaded.loaded.status ?? 500; - } - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); - - status = 500; - error = e; - } - - if (loaded && !error) { - branch.push(loaded); - } - - if (error) { - while (i--) { - if (route.b[i]) { - const index = /** @type {number} */ (route.b[i]); - const error_node = await options.manifest._.nodes[index](); - - /** @type {Loaded} */ - let node_loaded; - let j = i; - while (!(node_loaded = branch[j])) { - j -= 1; - } - - try { - const error_loaded = /** @type {import('./types').Loaded} */ ( - await load_node({ - ...opts, - node: error_node, - stuff: node_loaded.stuff, - is_error: true, - is_leaf: false, - status, - error - }) - ); - - if (error_loaded.loaded.error) { - continue; - } - - page_config = get_page_config(error_node.module, options); - branch = branch.slice(0, j + 1).concat(error_loaded); - stuff = { ...node_loaded.stuff, ...error_loaded.stuff }; - break ssr; - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); - - continue; - } - } - } - - // TODO backtrack until we find an __error.svelte component - // that we can use as the leaf node - // for now just return regular error page - return with_cookies( - await respond_with_error({ - event, - options, - state, - $session, - status, - error, - resolve_opts - }), - set_cookie_headers - ); - } - } - - if (loaded && loaded.loaded.stuff) { - stuff = { - ...stuff, - ...loaded.loaded.stuff - }; - } - } - } - - try { - return with_cookies( - await render_response({ - ...opts, - stuff, - event, - page_config, - status, - error, - branch: branch.filter(Boolean) - }), - set_cookie_headers - ); - } catch (err) { - const error = coalesce_to_error(err); - - options.handle_error(error, event); - - return with_cookies( - await respond_with_error({ - ...opts, - status: 500, - error - }), - set_cookie_headers - ); - } -} - -/** - * @param {import('types').SSRComponent} leaf - * @param {SSROptions} options - */ -function get_page_config(leaf, options) { - // TODO remove for 1.0 - if ('ssr' in leaf) { - throw new Error( - '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle' - ); - } - - return { - router: 'router' in leaf ? !!leaf.router : options.router, - hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate - }; -} - -/** - * @param {Response} response - * @param {string[]} set_cookie_headers - */ -function with_cookies(response, set_cookie_headers) { - if (set_cookie_headers.length) { - set_cookie_headers.forEach((value) => { - response.headers.append('set-cookie', value); - }); - } - return response; -} From 0dd38625658dcda602fd19955852ace65f7fcd95 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 17:58:40 -0400 Subject: [PATCH 021/110] purge all references to stuff --- .../templates/default/src/app.d.ts | 2 - .../templates/skeleton/src/app.d.ts | 1 - packages/kit/src/runtime/client/client.js | 70 +++---------------- packages/kit/src/runtime/client/types.d.ts | 3 - packages/kit/src/runtime/load.js | 8 --- packages/kit/src/runtime/server/index.js | 1 - packages/kit/src/runtime/server/page/index.js | 14 ---- .../kit/src/runtime/server/page/load_node.js | 4 -- .../kit/src/runtime/server/page/render.js | 5 +- .../runtime/server/page/respond_with_error.js | 5 -- .../kit/src/runtime/server/page/types.d.ts | 1 - packages/kit/test/apps/basics/src/app.d.ts | 9 --- .../kit/test/apps/options/source/app.d.ts | 2 - packages/kit/test/apps/writes/src/app.d.ts | 2 - packages/kit/types/ambient.d.ts | 7 -- packages/kit/types/index.d.ts | 22 ++---- 16 files changed, 14 insertions(+), 142 deletions(-) diff --git a/packages/create-svelte/templates/default/src/app.d.ts b/packages/create-svelte/templates/default/src/app.d.ts index 76372f545e22..f7e052adaabc 100644 --- a/packages/create-svelte/templates/default/src/app.d.ts +++ b/packages/create-svelte/templates/default/src/app.d.ts @@ -13,6 +13,4 @@ declare namespace App { // interface PublicEnv {} // interface Session {} - - // interface Stuff {} } diff --git a/packages/create-svelte/templates/skeleton/src/app.d.ts b/packages/create-svelte/templates/skeleton/src/app.d.ts index f69cbcbf5910..f2845e42d8fb 100644 --- a/packages/create-svelte/templates/skeleton/src/app.d.ts +++ b/packages/create-svelte/templates/skeleton/src/app.d.ts @@ -7,5 +7,4 @@ declare namespace App { // interface PrivateEnv {} // interface PublicEnv {} // interface Session {} - // interface Stuff {} } diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 5f1db7986e7e..8eda7632c5bd 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -91,7 +91,6 @@ export function create_client({ target, session, base, trailing_slash }) { branch: [], error: null, session_id: 0, - stuff: root_stuff, // @ts-ignore - we need the initial value to be null url: null }; @@ -391,7 +390,6 @@ export function create_client({ target, session, base, trailing_slash }) { * @param {{ * url: URL; * params: Record; - * stuff: Record; * branch: Array; * status: number; * error: Error | null; @@ -401,7 +399,6 @@ export function create_client({ target, session, base, trailing_slash }) { async function get_navigation_result_from_branch({ url, params, - stuff, branch, status, error, @@ -418,7 +415,6 @@ export function create_client({ target, session, base, trailing_slash }) { params, branch, error, - stuff, session_id }, props: { @@ -434,14 +430,10 @@ export function create_client({ target, session, base, trailing_slash }) { } } - const page_changed = - !current.url || - url.href !== current.url.href || - current.error !== error || - current.stuff !== stuff; + const page_changed = !current.url || url.href !== current.url.href || current.error !== error; if (page_changed) { - result.props.page = { error, params, routeId, status, stuff, url }; + result.props.page = { error, params, routeId, status, url }; // TODO remove this for 1.0 /** @@ -498,12 +490,11 @@ export function create_client({ target, session, base, trailing_slash }) { * module: import('types').CSRComponent; * url: URL; * params: Record; - * stuff: Record; * props?: Record; * routeId: string | null; * }} options */ - async function load_node({ status, error, module, url, params, stuff, props, routeId }) { + async function load_node({ status, error, module, url, params, props, routeId }) { /** @type {import('./types').BranchNode} */ const node = { module, @@ -511,11 +502,9 @@ export function create_client({ target, session, base, trailing_slash }) { params: new Set(), url: false, session: false, - stuff: false, dependencies: new Set() }, - loaded: null, - stuff + loaded: null }; /** @param dep {string} */ @@ -549,7 +538,7 @@ export function create_client({ target, session, base, trailing_slash }) { const load_input = { routeId, params: uses_params, - props: props || {}, + data: data || {}, get url() { node.uses.url = true; return load_url; @@ -558,9 +547,9 @@ export function create_client({ target, session, base, trailing_slash }) { node.uses.session = true; return session; }, + // @ts-expect-error get stuff() { - node.uses.stuff = true; - return { ...stuff }; + throw new Error('@migration task: Remove stuff (TODO link)'); }, async fetch(resource, init) { let requested; @@ -625,7 +614,6 @@ export function create_client({ target, session, base, trailing_slash }) { node.loaded = normalize(await module.load.call(null, load_input)); } - if (node.loaded.stuff) node.stuff = node.loaded.stuff; if (node.loaded.dependencies) { node.loaded.dependencies.forEach(add_dependency); } @@ -661,10 +649,6 @@ export function create_client({ target, session, base, trailing_slash }) { /** @type {Array} */ let branch = []; - /** @type {Record} */ - let stuff = root_stuff; - let stuff_changed = false; - /** @type {number} */ let status = 200; @@ -692,8 +676,7 @@ export function create_client({ target, session, base, trailing_slash }) { (changed.url && previous.uses.url) || changed.params.some((param) => previous.uses.params.has(param)) || (changed.session && previous.uses.session) || - Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))) || - (stuff_changed && previous.uses.stuff); + Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))); if (changed_since_last_render) { /** @type {Record} */ @@ -739,7 +722,6 @@ export function create_client({ target, session, base, trailing_slash }) { url, params, props, - stuff, routeId: route.id }); } @@ -762,10 +744,6 @@ export function create_client({ target, session, base, trailing_slash }) { state: current }; } - - if (node.loaded.stuff) { - stuff_changed = true; - } } } } else { @@ -795,7 +773,6 @@ export function create_client({ target, session, base, trailing_slash }) { module: await b[i](), url, params, - stuff: node_loaded.stuff, routeId: route.id }); @@ -803,13 +780,6 @@ export function create_client({ target, session, base, trailing_slash }) { continue; } - if (error_loaded?.loaded?.stuff) { - stuff = { - ...stuff, - ...error_loaded.loaded.stuff - }; - } - branch = branch.slice(0, j + 1).concat(error_loaded); break load; } catch (e) { @@ -825,13 +795,6 @@ export function create_client({ target, session, base, trailing_slash }) { routeId: route.id }); } else { - if (node?.loaded?.stuff) { - stuff = { - ...stuff, - ...node.loaded.stuff - }; - } - branch.push(node); } } @@ -839,7 +802,6 @@ export function create_client({ target, session, base, trailing_slash }) { return await get_navigation_result_from_branch({ url, params, - stuff, branch, status, error, @@ -863,7 +825,6 @@ export function create_client({ target, session, base, trailing_slash }) { module: await default_layout, url, params, - stuff: {}, routeId }); @@ -873,17 +834,12 @@ export function create_client({ target, session, base, trailing_slash }) { module: await default_error, url, params, - stuff: (root_layout && root_layout.loaded && root_layout.loaded.stuff) || {}, routeId }); return await get_navigation_result_from_branch({ url, params, - stuff: { - ...root_layout?.loaded?.stuff, - ...root_error?.loaded?.stuff - }, branch: [root_layout, root_error], status, error, @@ -1256,9 +1212,6 @@ export function create_client({ target, session, base, trailing_slash }) { /** @type {Array} */ const branch = []; - /** @type {Record} */ - let stuff = {}; - /** @type {import('./types').NavigationResult | undefined} */ let result; @@ -1281,7 +1234,6 @@ export function create_client({ target, session, base, trailing_slash }) { module: await components[nodes[i]](), url, params, - stuff, status: is_leaf ? status : undefined, error: is_leaf ? error : undefined, props, @@ -1304,11 +1256,6 @@ export function create_client({ target, session, base, trailing_slash }) { url, routeId }; - } else if (node.loaded.stuff) { - stuff = { - ...stuff, - ...node.loaded.stuff - }; } } } @@ -1318,7 +1265,6 @@ export function create_client({ target, session, base, trailing_slash }) { : await get_navigation_result_from_branch({ url, params, - stuff, branch, status, error, diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index ee5565061dac..68e4ac48a4a1 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -61,10 +61,8 @@ export type BranchNode = { params: Set; url: boolean; // TODO make more granular? session: boolean; - stuff: boolean; dependencies: Set; }; - stuff: Record; }; export type NavigationState = { @@ -72,6 +70,5 @@ export type NavigationState = { error: Error | null; params: Record; session_id: number; - stuff: Record; url: URL; }; diff --git a/packages/kit/src/runtime/load.js b/packages/kit/src/runtime/load.js index 974ba0ff104a..d1ab2b863ceb 100644 --- a/packages/kit/src/runtime/load.js +++ b/packages/kit/src/runtime/load.js @@ -72,13 +72,5 @@ export function normalize(loaded) { } } - // TODO remove before 1.0 - if (/** @type {any} */ (loaded).context) { - throw new Error( - 'You are returning "context" from a load function. ' + - '"context" was renamed to "stuff", please adjust your code accordingly.' - ); - } - return /** @type {import('types').NormalizedLoadOutput} */ (loaded); } diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index eb380947c65a..497a49c8c561 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -202,7 +202,6 @@ export async function respond(request, options, state) { state, $session: await options.hooks.getSession(event), page_config: { router: true, hydrate: true }, - stuff: {}, status: 200, error: null, branch: [], diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 5b1b47816478..9ecee9e43700 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -51,7 +51,6 @@ export async function render_page(event, route, options, state, resolve_opts) { status: 200, error: null, event, - stuff: {}, options, state, $session, @@ -109,8 +108,6 @@ export async function render_page(event, route, options, state, resolve_opts) { /** @type {string[]} */ let set_cookie_headers = []; - let stuff = {}; - ssr: { for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; @@ -127,7 +124,6 @@ export async function render_page(event, route, options, state, resolve_opts) { route, $session, node, - stuff, is_error: false, is_leaf: i === nodes.length - 1 }); @@ -185,7 +181,6 @@ export async function render_page(event, route, options, state, resolve_opts) { route, $session, node: error_node, - stuff: node_loaded.stuff, is_error: true, is_leaf: false, status, @@ -199,7 +194,6 @@ export async function render_page(event, route, options, state, resolve_opts) { page_config = get_page_config(error_node.module, options); branch = branch.slice(0, j + 1).concat(error_loaded); - stuff = { ...node_loaded.stuff, ...error_loaded.stuff }; break ssr; } catch (err) { const e = coalesce_to_error(err); @@ -228,13 +222,6 @@ export async function render_page(event, route, options, state, resolve_opts) { ); } } - - if (loaded && loaded.loaded.stuff) { - stuff = { - ...stuff, - ...loaded.loaded.stuff - }; - } } } @@ -246,7 +233,6 @@ export async function render_page(event, route, options, state, resolve_opts) { state, $session, resolve_opts, - stuff, page_config, status, error, diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index fbf789222522..68db5143f094 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -16,7 +16,6 @@ import { domain_matches, path_matches } from './cookie.js'; * route: import('types').SSRPage | import('types').SSRErrorPage; * node: import('types').SSRNode; * $session: any; - * stuff: Record; * is_error: boolean; * is_leaf: boolean; * status?: number; @@ -31,7 +30,6 @@ export async function load_node({ route, node, $session, - stuff, is_error, is_leaf, status, @@ -343,7 +341,6 @@ export async function load_node({ return proxy; }, - stuff: { ...stuff }, status: (is_error ? status : shadow.status) ?? null, error: is_error ? error ?? null : null }; @@ -384,7 +381,6 @@ export async function load_node({ node, props: shadow.body, loaded, - stuff: loaded.stuff || stuff, fetched, set_cookie_headers: new_cookies.map((new_cookie) => { const { name, value, ...options } = new_cookie; diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 2d1d2e42566d..bf6abfc0e16a 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -27,7 +27,6 @@ const updated = { * error: Error | null; * event: import('types').RequestEvent; * resolve_opts: import('types').RequiredResolveOptions; - * stuff: Record; * }} opts */ export async function render_response({ @@ -39,8 +38,7 @@ export async function render_response({ status, error = null, event, - resolve_opts, - stuff + resolve_opts }) { if (state.prerendering) { if (options.csp.mode === 'nonce') { @@ -126,7 +124,6 @@ export async function render_response({ params: event.params, routeId: event.routeId, status, - stuff, url: state.prerendering ? new PrerenderingURL(event.url) : event.url }, components: branch.map(({ node }) => node.module.default) 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 c3defa2d244e..69e1c04c259c 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -31,7 +31,6 @@ export async function respond_with_error({ }) { try { const branch = []; - let stuff = {}; if (resolve_opts.ssr) { const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout @@ -45,7 +44,6 @@ export async function respond_with_error({ route: GENERIC_ERROR, node: default_layout, $session, - stuff: {}, is_error: false, is_leaf: false }) @@ -63,7 +61,6 @@ export async function respond_with_error({ route: GENERIC_ERROR, node: default_error, $session, - stuff: layout_loaded ? layout_loaded.stuff : {}, is_error: true, is_leaf: false, status, @@ -72,7 +69,6 @@ export async function respond_with_error({ ); branch.push(layout_loaded, error_loaded); - stuff = error_loaded.stuff; } return await render_response({ @@ -83,7 +79,6 @@ export async function respond_with_error({ hydrate: options.hydrate, router: options.router }, - stuff, status, error, branch, diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 6559f88d462f..4a2e713cdee5 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -15,7 +15,6 @@ export type Loaded = { node: SSRNode; props: JSONValue | undefined; loaded: NormalizedLoadOutput; - stuff: Record; fetched: Fetched[]; set_cookie_headers: string[]; uses_credentials: boolean; diff --git a/packages/kit/test/apps/basics/src/app.d.ts b/packages/kit/test/apps/basics/src/app.d.ts index 9b0513a74bdf..9ab8200fbbc4 100644 --- a/packages/kit/test/apps/basics/src/app.d.ts +++ b/packages/kit/test/apps/basics/src/app.d.ts @@ -12,13 +12,4 @@ declare namespace App { answer?: number; calls?: number; } - - interface Stuff { - message: string; - error: string; - page: string; - value: number; - x: string; - y: string; - } } diff --git a/packages/kit/test/apps/options/source/app.d.ts b/packages/kit/test/apps/options/source/app.d.ts index d8c081b14d05..71207691df42 100644 --- a/packages/kit/test/apps/options/source/app.d.ts +++ b/packages/kit/test/apps/options/source/app.d.ts @@ -6,6 +6,4 @@ declare namespace App { interface Platform {} interface Session {} - - interface Stuff {} } diff --git a/packages/kit/test/apps/writes/src/app.d.ts b/packages/kit/test/apps/writes/src/app.d.ts index d8c081b14d05..71207691df42 100644 --- a/packages/kit/test/apps/writes/src/app.d.ts +++ b/packages/kit/test/apps/writes/src/app.d.ts @@ -6,6 +6,4 @@ declare namespace App { interface Platform {} interface Session {} - - interface Stuff {} } diff --git a/packages/kit/types/ambient.d.ts b/packages/kit/types/ambient.d.ts index 00c18066a3ed..d89064b09a85 100644 --- a/packages/kit/types/ambient.d.ts +++ b/packages/kit/types/ambient.d.ts @@ -14,8 +14,6 @@ * interface PublicEnv {} * * interface Session {} - * - * interface Stuff {} * } * ``` * @@ -70,11 +68,6 @@ declare namespace App { * The interface that defines `session`, both as an argument to [`load`](https://kit.svelte.dev/docs/loading) functions and the value of the [session store](https://kit.svelte.dev/docs/modules#$app-stores). */ export interface Session {} - - /** - * The interface that defines `stuff`, as input or output to [`load`](https://kit.svelte.dev/docs/loading) or as the value of the `stuff` property of the [page store](https://kit.svelte.dev/docs/modules#$app-stores). - */ - export interface Stuff {} } /** diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index c345207b39ce..234df3b56b37 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -177,37 +177,26 @@ export interface HandleError { */ export interface Load< Params extends Record = Record, - InputProps extends Record = Record, - OutputProps extends Record = InputProps + InputData extends Record = Record, + OutputData extends Record = Record > { - (event: LoadEvent): MaybePromise | void>; + (event: LoadEvent): MaybePromise; } export interface LoadEvent< Params extends Record = Record, - Props extends Record = Record + Data extends Record = Record > { fetch(info: RequestInfo, init?: RequestInit): Promise; params: Params; - props: Props; + data: Data; routeId: string | null; session: App.Session; - stuff: Partial; url: URL; status: number | null; error: Error | null; } -export interface LoadOutput = Record> { - status?: number; - error?: string | Error; - redirect?: string; - props?: Props; - stuff?: Partial; - cache?: LoadOutputCache; - dependencies?: string[]; -} - export interface LoadOutputCache { maxage: number; private?: boolean; @@ -222,7 +211,6 @@ export interface Page = Record Date: Mon, 1 Aug 2022 20:31:46 -0400 Subject: [PATCH 022/110] simplify --- packages/kit/src/runtime/server/page/index.js | 286 ++++++++---------- .../kit/src/runtime/server/page/load_node.js | 5 - .../kit/src/runtime/server/page/types.d.ts | 1 - 3 files changed, 121 insertions(+), 171 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 9ecee9e43700..568454988a98 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -38,6 +38,8 @@ export async function render_page(event, route, options, state, resolve_opts) { const $session = await options.hooks.getSession(event); + // TODO for non-GET requests, first call handler in +page.server.js + /** @type {Array} */ let nodes; @@ -63,153 +65,129 @@ export async function render_page(event, route, options, state, resolve_opts) { // we use == here rather than === because [undefined] serializes as "[null]" route.a.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) ); - } catch (err) { - const error = coalesce_to_error(err); - options.handle_error(error, event); + // the leaf node will be present. only layouts may be undefined + const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module; - return await respond_with_error({ - event, - options, - state, - $session, - status: 500, - error, - resolve_opts - }); - } - - // the leaf node will be present. only layouts may be undefined - const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module; + let page_config = get_page_config(leaf, options); - let page_config = get_page_config(leaf, options); - - if (state.prerendering) { - // if the page isn't marked as prerenderable (or is explicitly - // marked NOT prerenderable, if `prerender.default` is `true`), - // then bail out at this point - const should_prerender = leaf.prerender ?? options.prerender.default; - if (!should_prerender) { - return new Response(undefined, { - status: 204 - }); + if (state.prerendering) { + // if the page isn't marked as prerenderable (or is explicitly + // marked NOT prerenderable, if `prerender.default` is `true`), + // then bail out at this point + const should_prerender = leaf.prerender ?? options.prerender.default; + if (!should_prerender) { + return new Response(undefined, { + status: 204 + }); + } } - } - - /** @type {Array} */ - let branch = []; - /** @type {number} */ - let status = 200; + /** @type {Array} */ + let branch = []; - /** @type {Error | null} */ - let error = null; + /** @type {number} */ + let status = 200; - /** @type {string[]} */ - let set_cookie_headers = []; + /** @type {Error | null} */ + let error = null; - ssr: { - for (let i = 0; i < nodes.length; i += 1) { - const node = nodes[i]; + ssr: { + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; - /** @type {Loaded | undefined} */ - let loaded; + /** @type {Loaded | undefined} */ + let loaded; - if (node) { - try { - loaded = await load_node({ - event, - options, - state, - route, - $session, - node, - is_error: false, - is_leaf: i === nodes.length - 1 - }); - - set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers); + if (node) { + try { + loaded = await load_node({ + event, + options, + state, + route, + $session, + node, + is_error: false, + is_leaf: i === nodes.length - 1 + }); - if (loaded.loaded.redirect) { - return with_cookies( - new Response(undefined, { + if (loaded.loaded.redirect) { + return new Response(undefined, { status: loaded.loaded.status, headers: { location: loaded.loaded.redirect } - }), - set_cookie_headers - ); - } - - if (loaded.loaded.error) { - error = loaded.loaded.error; - status = loaded.loaded.status ?? 500; - } - } catch (err) { - const e = coalesce_to_error(err); + }); + } - options.handle_error(e, event); + if (loaded.loaded.error) { + error = loaded.loaded.error; + status = loaded.loaded.status ?? 500; + } + } catch (err) { + const e = coalesce_to_error(err); - status = 500; - error = e; - } + options.handle_error(e, event); - if (loaded && !error) { - branch.push(loaded); - } + status = 500; + error = e; + } - if (error) { - while (i--) { - if (route.b[i]) { - const index = /** @type {number} */ (route.b[i]); - const error_node = await options.manifest._.nodes[index](); - - /** @type {Loaded} */ - let node_loaded; - let j = i; - while (!(node_loaded = branch[j])) { - j -= 1; - } + if (loaded && !error) { + branch.push(loaded); + } - try { - const error_loaded = /** @type {import('./types').Loaded} */ ( - await load_node({ - event, - options, - state, - route, - $session, - node: error_node, - is_error: true, - is_leaf: false, - status, - error - }) - ); - - if (error_loaded.loaded.error) { - continue; + if (error) { + while (i--) { + if (route.b[i]) { + const index = /** @type {number} */ (route.b[i]); + const error_node = await options.manifest._.nodes[index](); + + /** @type {Loaded} */ + let node_loaded; + let j = i; + while (!(node_loaded = branch[j])) { + j -= 1; } - page_config = get_page_config(error_node.module, options); - branch = branch.slice(0, j + 1).concat(error_loaded); - break ssr; - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); + try { + const error_loaded = /** @type {import('./types').Loaded} */ ( + await load_node({ + event, + options, + state, + route, + $session, + node: error_node, + is_error: true, + is_leaf: false, + status, + error + }) + ); + + if (error_loaded.loaded.error) { + continue; + } + + page_config = get_page_config(error_node.module, options); + branch = branch.slice(0, j + 1).concat(error_loaded); + break ssr; + } catch (err) { + const e = coalesce_to_error(err); + + options.handle_error(e, event); - continue; + continue; + } } } - } - // TODO backtrack until we find an __error.svelte component - // that we can use as the leaf node - // for now just return regular error page - return with_cookies( - await respond_with_error({ + // TODO backtrack until we find an __error.svelte component + // that we can use as the leaf node + // for now just return regular error page + return await respond_with_error({ event, options, state, @@ -217,46 +195,37 @@ export async function render_page(event, route, options, state, resolve_opts) { status, error, resolve_opts - }), - set_cookie_headers - ); + }); + } } } } - } - try { - return with_cookies( - await render_response({ - event, - options, - state, - $session, - resolve_opts, - page_config, - status, - error, - branch: branch.filter(Boolean) - }), - set_cookie_headers - ); + return await render_response({ + event, + options, + state, + $session, + resolve_opts, + page_config, + status, + error, + branch: branch.filter(Boolean) + }); } catch (err) { const error = coalesce_to_error(err); options.handle_error(error, event); - return with_cookies( - await respond_with_error({ - event, - options, - state, - $session, - resolve_opts, - status: 500, - error - }), - set_cookie_headers - ); + return await respond_with_error({ + event, + options, + state, + $session, + status: 500, + error, + resolve_opts + }); } } @@ -277,16 +246,3 @@ function get_page_config(leaf, options) { hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate }; } - -/** - * @param {Response} response - * @param {string[]} set_cookie_headers - */ -function with_cookies(response, set_cookie_headers) { - if (set_cookie_headers.length) { - set_cookie_headers.forEach((value) => { - response.headers.append('set-cookie', value); - }); - } - return response; -} diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 68db5143f094..93980a3c974d 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -382,11 +382,6 @@ export async function load_node({ props: shadow.body, loaded, fetched, - set_cookie_headers: new_cookies.map((new_cookie) => { - const { name, value, ...options } = new_cookie; - // @ts-expect-error - return cookie.serialize(name, value, options); - }), uses_credentials }; } diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 4a2e713cdee5..34e1a8732c65 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -16,7 +16,6 @@ export type Loaded = { props: JSONValue | undefined; loaded: NormalizedLoadOutput; fetched: Fetched[]; - set_cookie_headers: string[]; uses_credentials: boolean; }; From f7ccfeee16dd415d5a81e1c464a0f058a0174cac Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 22:20:25 -0400 Subject: [PATCH 023/110] vite build completes, albeit with a broken build --- .../kit/src/core/generate_manifest/index.js | 40 +++------ .../core/sync/create_manifest_data/index.js | 34 ++++---- .../core/sync/create_manifest_data/types.d.ts | 2 +- packages/kit/src/core/sync/sync.js | 4 +- .../src/core/sync/write_client_manifest.js | 70 ++++++++++++++++ packages/kit/src/core/sync/write_manifest.js | 82 ------------------ packages/kit/src/core/sync/write_types.js | 70 ++++------------ packages/kit/src/runtime/client/client.js | 2 +- packages/kit/src/runtime/server/page/index.js | 7 +- packages/kit/src/vite/build/build_server.js | 84 +++++++++++-------- packages/kit/src/vite/index.js | 38 +++++---- packages/kit/types/internal.d.ts | 10 +-- 12 files changed, 192 insertions(+), 251 deletions(-) create mode 100644 packages/kit/src/core/sync/write_client_manifest.js delete mode 100644 packages/kit/src/core/sync/write_manifest.js diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 983c8501e99c..83e18c9bce60 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -14,33 +14,14 @@ import { get_mime_lookup } from '../utils.js'; */ export function generate_manifest({ build_data, relative_path, routes, format = 'esm' }) { /** @typedef {{ index: number, path: string }} LookupEntry */ - /** @type {Map} */ + /** @type {Map} */ const bundled_nodes = new Map(); - // 0 and 1 are special, they correspond to the root layout and root error nodes - bundled_nodes.set(build_data.manifest_data.components[0], { - path: `${relative_path}/nodes/0.js`, - index: 0 - }); - - bundled_nodes.set(build_data.manifest_data.components[1], { - path: `${relative_path}/nodes/1.js`, - index: 1 - }); - - routes.forEach((route) => { - if (route.type === 'page') { - [...route.a, ...route.b].forEach((component) => { - if (component && !bundled_nodes.has(component)) { - const i = build_data.manifest_data.components.indexOf(component); - - bundled_nodes.set(component, { - path: `${relative_path}/nodes/${i}.js`, - index: bundled_nodes.size - }); - } - }); - } + build_data.manifest_data.nodes.forEach((node, i) => { + bundled_nodes.set(node, { + path: `${relative_path}/nodes/0.js`, + index: i + }); }); /** @type {(path: string) => string} */ @@ -57,7 +38,7 @@ export function generate_manifest({ build_data, relative_path, routes, format = assets.push(build_data.service_worker); } - /** @param {string | undefined} id */ + /** @param {import('types').PageNode | undefined} id */ const get_index = (id) => id && /** @type {LookupEntry} */ (bundled_nodes.get(id)).index; const matchers = new Set(); @@ -87,10 +68,9 @@ export function generate_manifest({ build_data, relative_path, routes, format = pattern: ${pattern}, names: ${s(names)}, types: ${s(types)}, - path: ${route.path ? s(route.path) : null}, - shadow: ${route.shadow ? loader(`${relative_path}/${build_data.server.vite_manifest[route.shadow].file}`) : null}, - a: ${s(route.a.map(get_index))}, - b: ${s(route.b.map(get_index))} + errors: ${s(route.errors.map(get_index))}, + layouts: ${s(route.layouts.map(get_index))}, + page: ${s(get_index(route.page))} }`.replace(/^\t\t/gm, ''); } else { if (!build_data.server.vite_manifest[route.file]) { diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index ad4c273e9455..e108faa07a0b 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -35,7 +35,9 @@ export default function create_manifest_data({ // set default root layout/error tree.set('', { - error: posixify(path.relative(cwd, `${fallback}/error.svelte`)), + error: { + component: posixify(path.relative(cwd, `${fallback}/error.svelte`)) + }, layouts: { [DEFAULT]: default_layout } }); @@ -89,7 +91,10 @@ export default function create_manifest_data({ // +page files alphabetically, and will therefore be processes // before we reach the page if (item.kind === 'component' && item.is_error) { - tree_node(id).error = project_relative; + tree_node(id).error = { + component: project_relative + }; + return; } @@ -189,38 +194,30 @@ export default function create_manifest_data({ }); } - /** @type {string[]} */ - const components = []; - - /** @type {string[]} */ - const modules = []; + /** @type {import('types').PageNode[]} */ + const nodes = []; tree.forEach(({ layouts, error }) => { // we do [default, error, ...other_layouts] so that components[0] and [1] // are the root layout/error. kinda janky, there's probably a nicer way if (layouts[DEFAULT]) { - const { component, module } = layouts[DEFAULT]; - if (component) components.push(component); - if (module) modules.push(module); + nodes.push(layouts[DEFAULT]); } if (error) { - components.push(error); + nodes.push(error); } for (const id in layouts) { if (id !== DEFAULT) { - const { component, module } = layouts[id]; - if (component) components.push(component); - if (module) modules.push(module); + nodes.push(layouts[id]); } } }); route_map.forEach((route) => { if (route.type === 'page') { - if (route.page.component) components.push(route.page.component); - if (route.page.module) modules.push(route.page.module); + nodes.push(route.page); } }); @@ -264,8 +261,7 @@ export default function create_manifest_data({ return { assets, - components, - modules, + nodes, routes, matchers }; @@ -325,7 +321,7 @@ function trace(tree, id, layout_id = DEFAULT, project_relative) { /** @type {Array} */ const layouts = []; - /** @type {Array} */ + /** @type {Array} */ const errors = []; const parts = id.split('/').filter(Boolean); diff --git a/packages/kit/src/core/sync/create_manifest_data/types.d.ts b/packages/kit/src/core/sync/create_manifest_data/types.d.ts index 0f8c2754884c..b9000aa328a2 100644 --- a/packages/kit/src/core/sync/create_manifest_data/types.d.ts +++ b/packages/kit/src/core/sync/create_manifest_data/types.d.ts @@ -8,7 +8,7 @@ interface Part { } interface RouteTreeNode { - error: string | undefined; + error: PageNode | undefined; layouts: Record; } diff --git a/packages/kit/src/core/sync/sync.js b/packages/kit/src/core/sync/sync.js index a7517f5f5a86..ffa9ee854dda 100644 --- a/packages/kit/src/core/sync/sync.js +++ b/packages/kit/src/core/sync/sync.js @@ -1,7 +1,7 @@ import path from 'path'; import create_manifest_data from './create_manifest_data/index.js'; import { copy_assets } from './copy_assets.js'; -import { write_manifest } from './write_manifest.js'; +import { write_client_manifest } from './write_client_manifest.js'; import { write_matchers } from './write_matchers.js'; import { write_root } from './write_root.js'; import { write_tsconfig } from './write_tsconfig.js'; @@ -30,7 +30,7 @@ export function update(config) { const output = path.join(config.kit.outDir, 'generated'); const base = path.relative('.', output); - write_manifest(manifest_data, base, output); + write_client_manifest(manifest_data, base, output); write_root(manifest_data, output); write_matchers(manifest_data, output); write_types(config, manifest_data); diff --git a/packages/kit/src/core/sync/write_client_manifest.js b/packages/kit/src/core/sync/write_client_manifest.js new file mode 100644 index 000000000000..23e48096e9a4 --- /dev/null +++ b/packages/kit/src/core/sync/write_client_manifest.js @@ -0,0 +1,70 @@ +import { relative } from 'path'; +import { s } from '../../utils/misc.js'; +import { trim, write_if_changed } from './utils.js'; + +/** + * Writes the client manifest to disk. The manifest is used to power the router. It contains the + * list of routes and corresponding Svelte components (i.e. pages and layouts). + * @param {import('types').ManifestData} manifest_data + * @param {string} base + * @param {string} output + */ +export function write_client_manifest(manifest_data, base, output) { + /** @type {Map} */ + const node_indexes = new Map(); + + /** @param {import('types').PageNode} node */ + function generate_node(node) { + const declarations = []; + + if (node.module) { + declarations.push(`export * from ${s(relative(`${output}/nodes`, node.module))};`); + } + + if (node.component) { + declarations.push( + `export { default as component } from ${s(relative(`${output}/nodes`, node.component))};` + ); + } + + return declarations.join('\n'); + } + + const nodes = manifest_data.nodes + .map((node, i) => { + node_indexes.set(node, i); + write_if_changed(`${output}/nodes/${i}.js`, generate_node(node)); + return `() => import('./nodes/${i}')`; + }) + .join(',\n\t'); + + const dictionary = `{ + ${manifest_data.routes + .map((route) => { + if (route.type === 'page') { + const errors = route.errors.map((node) => (node ? node_indexes.get(node) : '')).join(','); + + const nodes = [...route.layouts, route.page] + .map((node) => (node ? node_indexes.get(node) : '')) + .join(','); + + return `${s(route.id)}: [[${errors}], [${nodes}]]`; + } + }) + .filter(Boolean) + .join(',\n\t\t')} + }`.replace(/^\t/gm, ''); + + write_if_changed( + `${output}/client-manifest.js`, + trim(` + export { matchers } from './client-matchers.js'; + + export const nodes = [ + ${nodes} + ]; + + export const dictionary = ${dictionary}; + `) + ); +} diff --git a/packages/kit/src/core/sync/write_manifest.js b/packages/kit/src/core/sync/write_manifest.js deleted file mode 100644 index 6d78b8d3f53a..000000000000 --- a/packages/kit/src/core/sync/write_manifest.js +++ /dev/null @@ -1,82 +0,0 @@ -import path from 'path'; -import { s } from '../../utils/misc.js'; -import { trim, write_if_changed } from './utils.js'; - -/** - * Writes the client manifest to disk. The manifest is used to power the router. It contains the - * list of routes and corresponding Svelte components (i.e. pages and layouts). - * @param {import('types').ManifestData} manifest_data - * @param {string} base - * @param {string} output - */ -export function write_manifest(manifest_data, base, output) { - /** @type {Record} */ - const component_indexes = {}; - - /** @type {Record} */ - const module_indexes = {}; - - /** @param {string} c */ - const get_path = (c) => path.relative(base, c); - - const components = `[\n\t${manifest_data.components - .map((component, i) => { - component_indexes[component] = i; - return `() => import(${s(get_path(component))})`; - }) - .join(',\n\t')}\n]`; - - const modules = `[\n\t${manifest_data.modules - .map((module, i) => { - module_indexes[module] = i; - return `() => import(${s(get_path(module))})`; - }) - .join(',\n\t')}\n]`; - - /** - * Serialize a page node as a pair of indices into the - * `components` and `modules` arrays - * @param {import('types').PageNode | undefined} node - */ - function serialize_node(node) { - if (!node) return ','; - - const indices = [node.component ? component_indexes[node.component] : '']; - if (node.module) indices.push(module_indexes[node.module]); - return `[${indices.join(',')}]`; - } - - const dictionary = `{ - ${manifest_data.routes - .map((route) => { - if (route.type === 'page') { - const errors = `[${route.errors - .map((file) => file && component_indexes[file]) - .join(', ')}]`; - - const layouts = `[${route.layouts.map(serialize_node).join(', ')}]`; - - const page = serialize_node(route.page); - - const value = [errors, layouts, page].join(','); - - return `${s(route.id)}: [${value}]`; - } - }) - .filter(Boolean) - .join(',\n\t\t')} - }`.replace(/^\t/gm, ''); - - write_if_changed( - `${output}/client-manifest.js`, - trim(` - export { matchers } from './client-matchers.js'; - - export const components = ${components}; - - export const modules = ${modules}; - - export const dictionary = ${dictionary}; - `) - ); -} diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index a543cc2047d0..93c43104a079 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -14,9 +14,9 @@ export type RequestHandler = GenericRequestHandler<${arg} /** @param {string} arg */ const page = (arg) => ` export type Load< - InputProps extends Record = Record, - OutputProps extends Record = InputProps -> = GenericLoad<${arg}, InputProps, OutputProps>;`; + InputData extends Record = Record, + OutputData extends Record = InputData +> = GenericLoad<${arg}, InputData, OutputData>;`; /** * @param {import('types').ValidatedConfig} config @@ -25,57 +25,23 @@ export type Load< export function write_types(config, manifest_data) { rimraf(`${config.kit.outDir}/types`); - /** @type {Map} */ - const shadow_types = new Map(); - manifest_data.routes.forEach((route) => { - const file = route.type === 'endpoint' ? route.file : route.shadow; - - if (file) { - const ext = /** @type {string} */ ( - config.kit.moduleExtensions.find((ext) => file.endsWith(ext)) - ); - const key = file.slice(0, -ext.length); - shadow_types.set(key, { - params: parse_route_id(key).names, - type: route.type === 'endpoint' ? 'endpoint' : 'both' - }); - } - }); - - manifest_data.components.forEach((component) => { - if (component.startsWith('.')) return; // exclude fallback components - - const ext = /** @type {string} */ (config.extensions.find((ext) => component.endsWith(ext))); - const key = component.slice(0, -ext.length); - - if (!shadow_types.has(key)) { - shadow_types.set(key, { params: parse_route_id(key).names, type: 'page' }); - } - }); - - shadow_types.forEach(({ params, type }, key) => { - const arg = - params.length > 0 ? `{ ${params.map((param) => `${param}: string`).join('; ')} }` : '{}'; - - const imports = []; - const content = []; - - if (type !== 'page') { - imports.push('RequestHandler as GenericRequestHandler, ResponseBody'); - content.push(endpoint(arg)); + const declarations = []; + const params = parse_route_id(route.id).names; + + declarations.push( + `interface Params ${ + params.length > 0 ? `{ ${params.map((param) => `${param}: string`).join('; ')} }` : '{}' + }` + ); + + if (route.type === 'page') { + // TODO write Load, GET, etc + } else { + // TODO write RequestHandler } - if (type !== 'endpoint') { - imports.push('Load as GenericLoad'); - content.push(page(arg)); - } - - content.unshift(header(imports.join(', '))); - - const parts = (key || 'index').split('/'); - parts.push('__types', /** @type {string} */ (parts.pop())); - - write(`${config.kit.outDir}/types/${parts.join('/')}.d.ts`, content.join('\n').trim()); + // TODO src/routes needs to be in this path somehow + write(`${config.kit.outDir}/types/${route.id}/$types.d.ts`, declarations.join('\n').trim()); }); } diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 8eda7632c5bd..508c1e40cae6 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -15,7 +15,7 @@ import { lock_fetch, unlock_fetch, initial_fetch, native_fetch } from './fetcher import { parse } from './parse.js'; import Root from '__GENERATED__/root.svelte'; -import { components, dictionary, matchers } from '__GENERATED__/client-manifest.js'; +import { error_components, nodes, dictionary, matchers } from '__GENERATED__/client-manifest.js'; const SCROLL_KEY = 'sveltekit:scroll'; const INDEX_KEY = 'sveltekit:index'; diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 568454988a98..0fbacf08ab01 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -40,9 +40,6 @@ export async function render_page(event, route, options, state, resolve_opts) { // TODO for non-GET requests, first call handler in +page.server.js - /** @type {Array} */ - let nodes; - if (!resolve_opts.ssr) { return await render_response({ branch: [], @@ -61,9 +58,9 @@ export async function render_page(event, route, options, state, resolve_opts) { } try { - nodes = await Promise.all( + const layout_nodes = await Promise.all( // we use == here rather than === because [undefined] serializes as "[null]" - route.a.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) + route.layout.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) ); // the leaf node will be present. only layouts may be undefined diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index ae8fc4e454bc..1c2bc9d56ccc 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -166,10 +166,8 @@ export async function build_server(options, client) { // add entry points for every endpoint... manifest_data.routes.forEach((route) => { - const file = route.type === 'endpoint' ? route.file : route.shadow; - - if (file) { - const resolved = path.resolve(cwd, file); + if (route.type === 'endpoint') { + const resolved = path.resolve(cwd, route.file); const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); const name = posixify(path.join('entries/endpoints', relative.replace(/\.js$/, ''))); input[name] = resolved; @@ -177,14 +175,16 @@ export async function build_server(options, client) { }); // ...and every component used by pages... - manifest_data.components.forEach((file) => { - const resolved = path.resolve(cwd, file); - const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); - - const name = relative.startsWith('..') - ? posixify(path.join('entries/fallbacks', path.basename(file))) - : posixify(path.join('entries/pages', relative)); - input[name] = resolved; + manifest_data.nodes.forEach((node, i) => { + if (node.component) { + const resolved = path.resolve(cwd, node.component); + const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); + + const name = relative.startsWith('..') + ? posixify(path.join('entries/fallbacks', path.basename(node.component))) + : posixify(path.join('entries/pages', relative)); + input[name] = resolved; + } }); // ...and every matcher @@ -239,33 +239,47 @@ export async function build_server(options, client) { } }); - manifest_data.components.forEach((component, i) => { - const entry = find_deps(client.vite_manifest, component, true); - - const imports = [`import * as module from '../${vite_manifest[component].file}';`]; - - const exports = [ - 'export { module };', - `export const index = ${i};`, - `export const file = '${entry.file}';`, - `export const imports = ${s(entry.imports)};`, - `export const stylesheets = ${s(entry.stylesheets)};` - ]; + manifest_data.nodes.forEach((node, i) => { + /** @type {string[]} */ + const imports = []; /** @type {string[]} */ - const styles = []; - - entry.stylesheets.forEach((file) => { - if (stylesheet_lookup.has(file)) { - const index = stylesheet_lookup.get(file); - const name = `stylesheet_${index}`; - imports.push(`import ${name} from '../stylesheets/${index}.js';`); - styles.push(`\t${s(file)}: ${name}`); + const exports = []; + + if (node.component) { + const entry = find_deps(client.vite_manifest, node.component, true); + + exports.push( + `export { default as module } from '../${vite_manifest[node.component].file}';`, + `export const index = ${i};`, + `export const file = '${entry.file}';`, + `export const imports = ${s(entry.imports)};`, + `export const stylesheets = ${s(entry.stylesheets)};` + ); + + /** @type {string[]} */ + const styles = []; + + entry.stylesheets.forEach((file) => { + if (stylesheet_lookup.has(file)) { + const index = stylesheet_lookup.get(file); + const name = `stylesheet_${index}`; + imports.push(`import ${name} from '../stylesheets/${index}.js';`); + styles.push(`\t${s(file)}: ${name}`); + } + }); + + if (styles.length > 0) { + exports.push(`export const inline_styles = () => ({\n${styles.join(',\n')}\n});`); } - }); + } + + if (node.module) { + exports.push(`export * from '../${vite_manifest[node.module].file}';`); + } - if (styles.length > 0) { - exports.push(`export const inline_styles = () => ({\n${styles.join(',\n')}\n});`); + if (node.server) { + exports.push('export const server = true;'); } const out = `${output_dir}/server/nodes/${i}.js`; diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index e6d347d66b9b..224df144fd6a 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -126,17 +126,18 @@ function kit() { start: `${get_runtime_directory(svelte_config.kit)}/client/start.js` }; - // This step is optional — Vite/Rollup will create the necessary chunks - // for everything regardless — but it means that entry chunks reflect - // their location in the source code, which is helpful for debugging - manifest_data.components.forEach((file) => { - const resolved = path.resolve(cwd, file); - const relative = decodeURIComponent(path.relative(svelte_config.kit.files.routes, resolved)); - - const name = relative.startsWith('..') - ? path.basename(file) - : posixify(path.join('pages', relative)); - input[name] = resolved; + manifest_data.nodes.forEach((node) => { + if (node.component) { + const resolved = path.resolve(cwd, node.component); + const relative = decodeURIComponent( + path.relative(svelte_config.kit.files.routes, resolved) + ); + + const name = relative.startsWith('..') + ? path.basename(node.component) + : posixify(path.join('pages', relative)); + input[name] = resolved; + } }); return get_default_build_config({ @@ -282,19 +283,22 @@ function kit() { * then use this hook to kick off builds for the server and service worker. */ async writeBundle(_options, bundle) { - for (const file of manifest_data.components) { - const id = vite.normalizePath(path.resolve(file)); - const node = this.getModuleInfo(id); + manifest_data.nodes.forEach((node, i) => { + const id = vite.normalizePath( + path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) + ); - if (node) { + const module_node = this.getModuleInfo(id); + + if (module_node) { prevent_illegal_rollup_imports( this.getModuleInfo.bind(this), - node, + module_node, illegal_imports, svelte_config.kit.outDir ); } - } + }); const verbose = vite_config.logLevel === 'info'; log = logger({ diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 636ee09ab851..3b547f24af7d 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -109,7 +109,7 @@ export class InternalServer extends Server { export interface ManifestData { assets: Asset[]; - components: string[]; + nodes: PageNode[]; routes: RouteData[]; matchers: Record; } @@ -144,7 +144,7 @@ export interface PageData { type: 'page'; id: string; pattern: RegExp; - errors: Array; + errors: Array; layouts: Array; page: PageNode; } @@ -287,11 +287,7 @@ export interface SSRPage { pattern: RegExp; names: string[]; types: string[]; - shadow: - | null - | (() => Promise<{ - [method: string]: ShadowRequestHandler; - }>); + /** * plan a is to render 1 or more layout components followed by a leaf component. */ From 8f9e2584ceb198aaf3af3b787969b8c2648c60e8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 22:34:13 -0400 Subject: [PATCH 024/110] vite dev now starts --- packages/kit/src/vite/build/build_server.js | 2 +- packages/kit/src/vite/dev/index.js | 128 ++++++++++++-------- 2 files changed, 76 insertions(+), 54 deletions(-) diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 1c2bc9d56ccc..18dc0697523c 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -253,7 +253,7 @@ export async function build_server(options, client) { `export { default as module } from '../${vite_manifest[node.component].file}';`, `export const index = ${i};`, `export const file = '${entry.file}';`, - `export const imports = ${s(entry.imports)};`, + `export const imports = ${s(entry.imports)};`, // TODO do we need to get imports from +page.js/+layout.js as well? `export const stylesheets = ${s(entry.stylesheets)};` ); diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index 97abdebc10ba..ada7befc9dc2 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -53,60 +53,85 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { imports: [], stylesheets: [] }, - nodes: manifest_data.components.map((id, index) => { + nodes: manifest_data.nodes.map((node, index) => { return async () => { - const url = id.startsWith('..') ? `/@fs${path.posix.resolve(id)}` : `/${id}`; + /** @type {import('types').SSRNode} */ + const result = {}; - const module = /** @type {import('types').SSRComponent} */ ( - await vite.ssrLoadModule(url) - ); + if (node.component) { + const url = node.component.startsWith('..') + ? `/@fs${path.posix.resolve(node.component)}` + : `/${node.component}`; - const node = await vite.moduleGraph.getModuleByUrl(url); - if (!node) throw new Error(`Could not find node for ${url}`); + const { default: component } = /** @type {import('types').SSRComponent} */ ( + await vite.ssrLoadModule(url) + ); - prevent_illegal_vite_imports( - node, - illegal_imports, - [...svelte_config.extensions, ...svelte_config.kit.moduleExtensions], - svelte_config.kit.outDir - ); + const module_node = await vite.moduleGraph.getModuleByUrl(url); + if (!module_node) throw new Error(`Could not find node for ${url}`); - return { - module, - index, - file: url.endsWith('.svelte') ? url : url + '?import', - imports: [], - stylesheets: [], - // in dev we inline all styles to avoid FOUC - inline_styles: async () => { - const deps = new Set(); - await find_deps(vite, node, deps); - - /** @type {Record} */ - const styles = {}; - - for (const dep of deps) { - const parsed = new URL(dep.url, 'http://localhost/'); - const query = parsed.searchParams; - - if ( - style_pattern.test(dep.file) || - (query.has('svelte') && query.get('type') === 'style') - ) { - try { - const mod = await vite.ssrLoadModule(dep.url); - styles[dep.url] = mod.default; - } catch { - // this can happen with dynamically imported modules, I think - // because the Vite module graph doesn't distinguish between - // static and dynamic imports? TODO investigate, submit fix + prevent_illegal_vite_imports( + module_node, + illegal_imports, + [...svelte_config.extensions, ...svelte_config.kit.moduleExtensions], + svelte_config.kit.outDir + ); + + Object.assign(result, { + component, + index, + file: url.endsWith('.svelte') ? url : url + '?import', + imports: [], + stylesheets: [], + // in dev we inline all styles to avoid FOUC + inline_styles: async () => { + const deps = new Set(); + await find_deps(vite, module_node, deps); + + /** @type {Record} */ + const styles = {}; + + for (const dep of deps) { + const parsed = new URL(dep.url, 'http://localhost/'); + const query = parsed.searchParams; + + if ( + style_pattern.test(dep.file) || + (query.has('svelte') && query.get('type') === 'style') + ) { + try { + const mod = await vite.ssrLoadModule(dep.url); + styles[dep.url] = mod.default; + } catch { + // this can happen with dynamically imported modules, I think + // because the Vite module graph doesn't distinguish between + // static and dynamic imports? TODO investigate, submit fix + } } } + + return styles; } + }); + } - return styles; - } - }; + if (node.module) { + const url = node.module.startsWith('..') + ? `/@fs${path.posix.resolve(node.module)}` + : `/${node.module}`; + + const module = /** @type {import('types').SSRComponent} */ ( + await vite.ssrLoadModule(url) + ); + + Object.assign(result, module); + } + + if (node.server) { + result.server = true; + } + + return result; }; }), routes: manifest_data.routes.map((route) => { @@ -119,14 +144,11 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { pattern, names, types, - shadow: route.shadow - ? async () => { - const url = path.resolve(cwd, /** @type {string} */ (route.shadow)); - return await vite.ssrLoadModule(url); - } - : null, - a: route.a.map((id) => (id ? manifest_data.components.indexOf(id) : undefined)), - b: route.b.map((id) => (id ? manifest_data.components.indexOf(id) : undefined)) + errors: route.errors.map((id) => (id ? manifest_data.nodes.indexOf(id) : undefined)), + layouts: route.layouts.map((id) => + id ? manifest_data.nodes.indexOf(id) : undefined + ), + page: manifest_data.nodes.indexOf(route.page) }; } From 326753440e216e63c6c898e04d1421867cfd0058 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 22:44:39 -0400 Subject: [PATCH 025/110] now rendering a blank page --- packages/kit/src/runtime/server/page/index.js | 16 ++++++------- .../kit/src/runtime/server/page/load_node.js | 12 +++++----- .../kit/src/runtime/server/page/render.js | 2 +- packages/kit/types/internal.d.ts | 23 +++++++++---------- 4 files changed, 26 insertions(+), 27 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 0fbacf08ab01..604a0e584d2a 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -58,21 +58,21 @@ export async function render_page(event, route, options, state, resolve_opts) { } try { - const layout_nodes = await Promise.all( + const nodes = await Promise.all([ // we use == here rather than === because [undefined] serializes as "[null]" - route.layout.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())) - ); + ...route.layouts.map((n) => (n == undefined ? n : options.manifest._.nodes[n]())), + options.manifest._.nodes[route.page]() + ]); - // the leaf node will be present. only layouts may be undefined - const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module; + const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1)); - let page_config = get_page_config(leaf, options); + let page_config = get_page_config(leaf_node, options); if (state.prerendering) { // if the page isn't marked as prerenderable (or is explicitly // marked NOT prerenderable, if `prerender.default` is `true`), // then bail out at this point - const should_prerender = leaf.prerender ?? options.prerender.default; + const should_prerender = leaf_node.prerender ?? options.prerender.default; if (!should_prerender) { return new Response(undefined, { status: 204 @@ -227,7 +227,7 @@ export async function render_page(event, route, options, state, resolve_opts) { } /** - * @param {import('types').SSRComponent} leaf + * @param {import('types').SSRNode} leaf * @param {SSROptions} options */ function get_page_config(leaf, options) { diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 93980a3c974d..9f09bba6252c 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -35,7 +35,7 @@ export async function load_node({ status, error }) { - const { module } = node; + const { component } = node; let uses_credentials = false; @@ -50,7 +50,7 @@ export async function load_node({ /** @type {import('types').NormalizedLoadOutput} */ let loaded; - const should_prerender = node.module.prerender ?? options.prerender.default; + const should_prerender = node.prerender ?? options.prerender.default; /** @type {import('types').ShadowData} */ const shadow = is_leaf @@ -76,15 +76,15 @@ export async function load_node({ loaded = { redirect: shadow.redirect }; - } else if (module.load) { + } else if (node.load) { /** @type {import('types').LoadEvent} */ const load_input = { url: state.prerendering ? new PrerenderingURL(event.url) : new LoadURL(event.url), params: event.params, - props: shadow.body || {}, + data: {}, // TODO load from +page.server.js/+layout.server.js routeId: event.routeId, get session() { - if (node.module.prerender ?? options.prerender.default) { + if (node.prerender ?? options.prerender.default) { throw Error( 'Attempted to access session from a prerendered page. Session would never be populated.' ); @@ -354,7 +354,7 @@ export async function load_node({ }); } - loaded = normalize(await module.load.call(null, load_input)); + loaded = normalize(await node.load.call(null, load_input)); } else if (shadow.body) { loaded = { props: shadow.body diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index bf6abfc0e16a..d6a7a1e7a72e 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -126,7 +126,7 @@ export async function render_response({ status, url: state.prerendering ? new PrerenderingURL(event.url) : event.url }, - components: branch.map(({ node }) => node.module.default) + components: branch.map(({ node }) => node.component.default) }; // TODO remove this for 1.0 diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 3b547f24af7d..88c0b4136c6d 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -135,7 +135,7 @@ export interface NormalizedLoadOutputCache { } export interface PageNode { - component?: string; + component: string; // TODO supply default component if it's missing (bit of an edge case) module?: string; server?: string; } @@ -228,7 +228,7 @@ export interface SSREndpoint { } export interface SSRNode { - module: SSRComponent; + component: SSRComponent; /** index into the `components` array in client-manifest.js */ index: number; /** client-side module URL for this component */ @@ -239,6 +239,12 @@ export interface SSRNode { stylesheets: string[]; /** inlined styles */ inline_styles?: () => MaybePromise>; + + load?: Load; + hydrate?: boolean; + prerender?: boolean; + router?: boolean; + ssr?: boolean; } export type SSRNodeLoader = () => Promise; @@ -287,16 +293,9 @@ export interface SSRPage { pattern: RegExp; names: string[]; types: string[]; - - /** - * plan a is to render 1 or more layout components followed by a leaf component. - */ - a: Array; - /** - * plan b — if one of them components fails in `load` we backtrack until we find - * the nearest error component. - */ - b: Array; + errors: Array; + layouts: Array; + page: number; } export interface SSRErrorPage { From ebfee3b1553e6e35dec4c5c1499dce0304002413 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 23:02:10 -0400 Subject: [PATCH 026/110] rendering an error page --- .../core/sync/create_manifest_data/index.js | 2 +- .../kit/src/runtime/server/page/load_node.js | 2 -- .../kit/src/runtime/server/page/render.js | 2 +- packages/kit/src/vite/build/build_server.js | 2 +- packages/kit/src/vite/dev/index.js | 12 ++++----- .../apps/amp/src/routes/valid/+page.svelte | 4 +-- packages/kit/types/internal.d.ts | 25 ++++++++++++------- 7 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index e108faa07a0b..faee410b4cfe 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -295,7 +295,7 @@ function analyze(file, component_extensions, module_extensions) { const module_extension = module_extensions.find((ext) => file.endsWith(ext)); if (module_extension) { const name = file.slice(0, -module_extension.length); - const pattern = /^\+(server|(page(\.server)?)|(layout(-([a-zA-Z0-9_-]+))?(\.server)?))$/; + const pattern = /^\+(?:(server)|(page(\.server)?)|(layout(-([a-zA-Z0-9_-]+))?(\.server)?))$/; const match = pattern.exec(name); if (!match) return null; diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 9f09bba6252c..391e45dc0465 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -35,8 +35,6 @@ export async function load_node({ status, error }) { - const { component } = node; - let uses_credentials = false; /** @type {Array} */ diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index d6a7a1e7a72e..836a6a4a2fa1 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -126,7 +126,7 @@ export async function render_response({ status, url: state.prerendering ? new PrerenderingURL(event.url) : event.url }, - components: branch.map(({ node }) => node.component.default) + components: branch.map(({ node }) => node.component) }; // TODO remove this for 1.0 diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 18dc0697523c..053f9cfdb52b 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -279,7 +279,7 @@ export async function build_server(options, client) { } if (node.server) { - exports.push('export const server = true;'); + exports.push(`export * from '../${vite_manifest[node.server].file}';`); } const out = `${output_dir}/server/nodes/${i}.js`; diff --git a/packages/kit/src/vite/dev/index.js b/packages/kit/src/vite/dev/index.js index ada7befc9dc2..f10cb3624717 100644 --- a/packages/kit/src/vite/dev/index.js +++ b/packages/kit/src/vite/dev/index.js @@ -120,15 +120,15 @@ export async function dev(vite, vite_config, svelte_config, illegal_imports) { ? `/@fs${path.posix.resolve(node.module)}` : `/${node.module}`; - const module = /** @type {import('types').SSRComponent} */ ( - await vite.ssrLoadModule(url) - ); - - Object.assign(result, module); + result.module = await vite.ssrLoadModule(url); } if (node.server) { - result.server = true; + const url = node.server.startsWith('..') + ? `/@fs${path.posix.resolve(node.server)}` + : `/${node.server}`; + + result.server = await vite.ssrLoadModule(url); } return result; diff --git a/packages/kit/test/apps/amp/src/routes/valid/+page.svelte b/packages/kit/test/apps/amp/src/routes/valid/+page.svelte index 91ce7efd6c7a..2b5708e98477 100644 --- a/packages/kit/test/apps/amp/src/routes/valid/+page.svelte +++ b/packages/kit/test/apps/amp/src/routes/valid/+page.svelte @@ -1,9 +1,7 @@ - - diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 88c0b4136c6d..8445cdeb9725 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -198,10 +198,6 @@ export interface ShadowData { } export interface SSRComponent { - router?: boolean; - hydrate?: boolean; - prerender?: boolean; - load: Load; default: { render(props: Record): { html: string; @@ -240,11 +236,22 @@ export interface SSRNode { /** inlined styles */ inline_styles?: () => MaybePromise>; - load?: Load; - hydrate?: boolean; - prerender?: boolean; - router?: boolean; - ssr?: boolean; + module: { + load?: Load; + hydrate?: boolean; + prerender?: boolean; + router?: boolean; + ssr?: boolean; + }; + + server: { + // TODO create types for these + GET?: TODO; + POST?: TODO; + PATCH?: TODO; + PUT?: TODO; + DELETE?: TODO; + }; } export type SSRNodeLoader = () => Promise; From e232dd20af3177070678c8545cd375d731d10824 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 23:19:00 -0400 Subject: [PATCH 027/110] it renders html! --- packages/kit/src/runtime/server/page/index.js | 24 +- .../kit/src/runtime/server/page/load_node.js | 278 ++---------------- .../kit/src/runtime/server/page/render.js | 20 +- .../kit/src/runtime/server/page/types.d.ts | 5 +- packages/kit/types/index.d.ts | 3 +- 5 files changed, 38 insertions(+), 292 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 604a0e584d2a..fb397f7ec655 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -104,24 +104,8 @@ export async function render_page(event, route, options, state, resolve_opts) { state, route, $session, - node, - is_error: false, - is_leaf: i === nodes.length - 1 + node }); - - if (loaded.loaded.redirect) { - return new Response(undefined, { - status: loaded.loaded.status, - headers: { - location: loaded.loaded.redirect - } - }); - } - - if (loaded.loaded.error) { - error = loaded.loaded.error; - status = loaded.loaded.status ?? 500; - } } catch (err) { const e = coalesce_to_error(err); @@ -156,11 +140,7 @@ export async function render_page(event, route, options, state, resolve_opts) { state, route, $session, - node: error_node, - is_error: true, - is_leaf: false, - status, - error + node: error_node }) ); diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 391e45dc0465..748ed1397464 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -1,6 +1,5 @@ import * as cookie from 'cookie'; import * as set_cookie_parser from 'set-cookie-parser'; -import { normalize } from '../../load.js'; import { respond } from '../index.js'; import { LoadURL, PrerenderingURL, is_root_relative, resolve } from '../../../utils/url.js'; import { check_method_names, is_pojo, lowercase_keys } from '../utils.js'; @@ -16,27 +15,10 @@ import { domain_matches, path_matches } from './cookie.js'; * route: import('types').SSRPage | import('types').SSRErrorPage; * node: import('types').SSRNode; * $session: any; - * is_error: boolean; - * is_leaf: boolean; - * status?: number; - * error?: Error; * }} opts * @returns {Promise} */ -export async function load_node({ - event, - options, - state, - route, - node, - $session, - is_error, - is_leaf, - status, - error -}) { - let uses_credentials = false; - +export async function load_node({ event, options, state, route, node, $session }) { /** @type {Array} */ const fetched = []; @@ -45,49 +27,39 @@ export async function load_node({ /** @type {import('set-cookie-parser').Cookie[]} */ const new_cookies = []; - /** @type {import('types').NormalizedLoadOutput} */ - let loaded; - - const should_prerender = node.prerender ?? options.prerender.default; - - /** @type {import('types').ShadowData} */ - const shadow = is_leaf - ? await load_shadow_data( - /** @type {import('types').SSRPage} */ (route), - event, - options, - should_prerender - ) - : {}; - - if (shadow.cookies) { - shadow.cookies.forEach((header) => { - new_cookies.push(set_cookie_parser.parseString(header)); - }); + /** @type {Record} */ + let server_data; + + /** @type {Record} */ + let data; + + if (node.server) { + const should_prerender = node.module?.prerender ?? options.prerender.default; + const mod = node.server; + + if (should_prerender && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { + throw new Error('Cannot prerender pages that have endpoints with mutative methods'); + } + + // TODO unwrap top-level promises + server_data = await mod.GET.call(null, event); + } else { + server_data = {}; } - if (shadow.error) { - loaded = { - error: shadow.error - }; - } else if (shadow.redirect) { - loaded = { - redirect: shadow.redirect - }; - } else if (node.load) { + if (node.module?.load) { /** @type {import('types').LoadEvent} */ - const load_input = { + const load_event = { url: state.prerendering ? new PrerenderingURL(event.url) : new LoadURL(event.url), params: event.params, - data: {}, // TODO load from +page.server.js/+layout.server.js + data: server_data, routeId: event.routeId, get session() { - if (node.prerender ?? options.prerender.default) { + if (node.module.prerender ?? options.prerender.default) { throw Error( 'Attempted to access session from a prerendered page. Session would never be populated.' ); } - uses_credentials = true; return $session; }, /** @@ -171,8 +143,6 @@ export async function load_node({ } } else if (is_root_relative(resolved)) { if (opts.credentials !== 'omit') { - uses_credentials = true; - const authorization = event.request.headers.get('authorization'); // combine cookies from the initiating request with any that were @@ -239,8 +209,6 @@ export async function load_node({ `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) && opts.credentials !== 'omit' ) { - uses_credentials = true; - const cookie = event.request.headers.get('cookie'); if (cookie) opts.headers.set('cookie', cookie); } @@ -338,212 +306,28 @@ export async function load_node({ }); return proxy; - }, - status: (is_error ? status : shadow.status) ?? null, - error: is_error ? error ?? null : null + } }; if (options.dev) { // TODO remove this for 1.0 - Object.defineProperty(load_input, 'page', { + Object.defineProperty(load_event, 'page', { get: () => { throw new Error('`page` in `load` functions has been replaced by `url` and `params`'); } }); } - loaded = normalize(await node.load.call(null, load_input)); - } else if (shadow.body) { - loaded = { - props: shadow.body - }; + // TODO unwrap top-level promises + data = await node.module.load.call(null, load_event); } else { - loaded = {}; - } - - loaded.status = loaded.status ?? shadow.status; - - // generate __data.json files when prerendering - if (shadow.body && state.prerendering) { - const pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`; - - const dependency = { - response: new Response(undefined), - body: JSON.stringify(shadow.body) - }; - - state.prerendering.dependencies.set(pathname, dependency); + data = {}; } return { node, - props: shadow.body, - loaded, - fetched, - uses_credentials + data, + server_data, // we return this separately so it can be serialized into the page + fetched }; } - -/** - * - * @param {import('types').SSRPage} route - * @param {import('types').RequestEvent} event - * @param {import('types').SSROptions} options - * @param {boolean} prerender - * @returns {Promise} - */ -async function load_shadow_data(route, event, options, prerender) { - if (!route.shadow) return {}; - - try { - const mod = await route.shadow(); - - check_method_names(mod); - - if (prerender && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { - throw new Error('Cannot prerender pages that have endpoints with mutative methods'); - } - - const { method } = event.request; - const is_get = method === 'HEAD' || method === 'GET'; - const handler = method === 'HEAD' ? mod.HEAD || mod.GET : mod[method]; - - if (!handler && !is_get) { - return { - status: 405, - error: new Error(`${method} method not allowed`) - }; - } - - /** @type {import('types').ShadowData} */ - const data = { - status: undefined, - cookies: [], - body: {} - }; - - if (!is_get) { - const { status, headers, body } = validate_shadow_output(await handler(event)); - add_cookies(/** @type {string[]} */ (data.cookies), headers); - data.status = status; - - // explicit errors cause an error page... - if (body instanceof Error) { - if (status < 400) { - data.status = 500; - data.error = new Error('A non-error status code was returned with an error body'); - } else { - data.error = body; - } - - return data; - } - - // ...redirects are respected... - if (status >= 300 && status < 400) { - data.redirect = /** @type {string} */ ( - headers instanceof Headers ? headers.get('location') : headers.location - ); - return data; - } - - // ...but 4xx and 5xx status codes _don't_ result in the error page - // rendering for non-GET requests — instead, we allow the page - // to render with any validation errors etc that were returned - data.body = body; - } - - const get = (method === 'HEAD' && mod.HEAD) || mod.GET; - if (get) { - const { status, headers, body } = validate_shadow_output(await get(event)); - add_cookies(/** @type {string[]} */ (data.cookies), headers); - - if (body instanceof Error) { - if (status < 400) { - data.status = 500; - data.error = new Error('A non-error status code was returned with an error body'); - } else { - data.status = status; - data.error = body; - } - - return data; - } - - if (status >= 400) { - data.status = status; - data.error = new Error('Failed to load data'); - return data; - } - - if (status >= 300) { - data.status = status; - data.redirect = /** @type {string} */ ( - headers instanceof Headers ? headers.get('location') : headers.location - ); - return data; - } - - data.body = { ...body, ...data.body }; - } - - return data; - } catch (e) { - const error = coalesce_to_error(e); - options.handle_error(error, event); - - return { - status: 500, - error - }; - } -} - -/** - * @param {string[]} target - * @param {Partial} headers - */ -function add_cookies(target, headers) { - const cookies = headers['set-cookie']; - if (cookies) { - if (Array.isArray(cookies)) { - target.push(...cookies); - } else { - target.push(/** @type {string} */ (cookies)); - } - } -} - -/** - * @param {import('types').ShadowEndpointOutput} result - */ -function validate_shadow_output(result) { - // TODO remove for 1.0 - // @ts-expect-error - if (result.fallthrough) { - throw new Error( - 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching' - ); - } - - const { status = 200, body = {} } = result; - let headers = result.headers || {}; - - if (headers instanceof Headers) { - if (headers.has('set-cookie')) { - throw new Error( - 'Endpoint request handler cannot use Headers interface with Set-Cookie headers' - ); - } - } else { - headers = lowercase_keys(/** @type {Record} */ (headers)); - } - - if (!is_pojo(body)) { - throw new Error( - 'Body returned from endpoint request handler must be a plain object or an Error' - ); - } - - return { status, headers, body }; -} diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 836a6a4a2fa1..622da09b3889 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -62,8 +62,6 @@ export async function render_response({ /** @type {Array} */ const serialized_data = []; - let shadow_props; - let rendered; let is_private = false; @@ -79,13 +77,7 @@ export async function render_response({ if (resolve_opts.ssr) { const leaf = /** @type {import('./types.js').Loaded} */ (branch.at(-1)); - if (leaf.loaded.status) { - // explicit status returned from `load` or a page endpoint trumps - // initial status - status = leaf.loaded.status; - } - - for (const { node, props, loaded, fetched, uses_credentials } of branch) { + for (const { node, data, fetched } of branch) { if (node.imports) { node.imports.forEach((url) => modulepreloads.add(url)); } @@ -100,10 +92,6 @@ export async function render_response({ // TODO probably better if `fetched` wasn't populated unless `hydrate` if (fetched && page_config.hydrate) serialized_data.push(...fetched); - if (props) shadow_props = props; - - cache = loaded?.cache; - is_private = cache?.private ?? uses_credentials; } const session = writable($session); @@ -149,7 +137,7 @@ export async function render_response({ // props_n (instead of props[n]) makes it easy to avoid // unnecessary updates for layout components for (let i = 0; i < branch.length; i += 1) { - props[`props_${i}`] = await branch[i].loaded.props; + props[`props_${i}`] = { data: branch[i].data }; } rendered = options.root.render(props); @@ -258,10 +246,6 @@ export async function render_response({ ) ) .join('\n\t'); - - if (shadow_props) { - body += render_json_payload_script({ type: 'props' }, shadow_props); - } } if (options.service_worker) { diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 34e1a8732c65..09b47d0c971c 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -13,10 +13,9 @@ export type Fetched = { export type Loaded = { node: SSRNode; - props: JSONValue | undefined; - loaded: NormalizedLoadOutput; + data: Record; + server_data: JSONValue; fetched: Fetched[]; - uses_credentials: boolean; }; type CspMode = 'hash' | 'nonce' | 'auto'; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 234df3b56b37..f5b58c933177 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -193,8 +193,7 @@ export interface LoadEvent< routeId: string | null; session: App.Session; url: URL; - status: number | null; - error: Error | null; + // TODO parent, setHeaders, depends } export interface LoadOutputCache { From c546946cc46063c6c1f1107ec5a27160bf768a99 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 1 Aug 2022 23:27:19 -0400 Subject: [PATCH 028/110] tidy up --- .../kit/src/runtime/components/error.svelte | 27 +++++------------- .../kit/src/runtime/server/page/load_node.js | 2 -- .../runtime/server/page/respond_with_error.js | 28 +++++-------------- packages/kit/src/runtime/server/utils.js | 12 -------- packages/kit/src/runtime/server/utils.spec.js | 9 +----- 5 files changed, 15 insertions(+), 63 deletions(-) diff --git a/packages/kit/src/runtime/components/error.svelte b/packages/kit/src/runtime/components/error.svelte index 4c3541911212..f1d30a89bb79 100644 --- a/packages/kit/src/runtime/components/error.svelte +++ b/packages/kit/src/runtime/components/error.svelte @@ -1,29 +1,16 @@ - - -

{status}

+

{$page.status}

-
{error.message}
+
{$page.error.message}
-{#if error.frame} -
{error.frame}
+{#if $page.error.frame} +
{$page.error.frame}
{/if} -{#if error.stack} -
{error.stack}
+{#if $page.error.stack} +
{$page.error.stack}
{/if} diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 748ed1397464..7e59ab19eb95 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -2,8 +2,6 @@ import * as cookie from 'cookie'; import * as set_cookie_parser from 'set-cookie-parser'; import { respond } from '../index.js'; import { LoadURL, PrerenderingURL, is_root_relative, resolve } from '../../../utils/url.js'; -import { check_method_names, is_pojo, lowercase_keys } from '../utils.js'; -import { coalesce_to_error } from '../../../utils/error.js'; import { domain_matches, path_matches } from './cookie.js'; /** 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 69e1c04c259c..c915cb791996 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -43,30 +43,16 @@ export async function respond_with_error({ state, route: GENERIC_ERROR, node: default_layout, - $session, - is_error: false, - is_leaf: false + $session }) ); - if (layout_loaded.loaded.error) { - throw layout_loaded.loaded.error; - } - - const error_loaded = /** @type {Loaded} */ ( - await load_node({ - event, - options, - state, - route: GENERIC_ERROR, - node: default_error, - $session, - is_error: true, - is_leaf: false, - status, - error - }) - ); + const error_loaded = /** @type {Loaded} */ ({ + node: default_error, + data: {}, + server_data: {}, + fetched: [] + }); branch.push(layout_loaded, error_loaded); } diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index 406ad9edbcf8..3d9c1ba1d1cc 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -1,15 +1,3 @@ -/** @param {Record} obj */ -export function lowercase_keys(obj) { - /** @type {Record} */ - const clone = {}; - - for (const key in obj) { - clone[key.toLowerCase()] = obj[key]; - } - - return clone; -} - /** @param {any} body */ export function is_pojo(body) { if (typeof body !== 'object') return false; diff --git a/packages/kit/src/runtime/server/utils.spec.js b/packages/kit/src/runtime/server/utils.spec.js index 2748a8b24d6c..8ec55eefe025 100644 --- a/packages/kit/src/runtime/server/utils.spec.js +++ b/packages/kit/src/runtime/server/utils.spec.js @@ -1,13 +1,6 @@ import { test } from 'uvu'; import * as assert from 'uvu/assert'; -import { lowercase_keys, serialize_error } from './utils.js'; - -test('lowercase_keys', () => { - assert.equal(lowercase_keys({ KEY: 'value' }), { key: 'value' }); - assert.equal(lowercase_keys({ Key: 'value' }), { key: 'value' }); - assert.equal(lowercase_keys({ UNDERSCORE_KEY: 'value' }), { underscore_key: 'value' }); - assert.equal(lowercase_keys({ 1: 'Hello World' }), { 1: 'Hello World' }); -}); +import { serialize_error } from './utils.js'; test('serialize_error', () => { class FancyError extends Error { From f3cf58b2eb1f4795828bac964c0972429c995b46 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 06:23:20 -0400 Subject: [PATCH 029/110] get error page rendering correctly --- packages/kit/src/runtime/server/page/index.js | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index fb397f7ec655..492a02a8c682 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -121,8 +121,8 @@ export async function render_page(event, route, options, state, resolve_opts) { if (error) { while (i--) { - if (route.b[i]) { - const index = /** @type {number} */ (route.b[i]); + if (route.errors[i]) { + const index = /** @type {number} */ (route.errors[i]); const error_node = await options.manifest._.nodes[index](); /** @type {Loaded} */ @@ -133,22 +133,14 @@ export async function render_page(event, route, options, state, resolve_opts) { } try { - const error_loaded = /** @type {import('./types').Loaded} */ ( - await load_node({ - event, - options, - state, - route, - $session, - node: error_node - }) - ); - - if (error_loaded.loaded.error) { - continue; - } - - page_config = get_page_config(error_node.module, options); + const error_loaded = /** @type {import('./types').Loaded} */ ({ + node: error_node, + data: {}, + server_data: {}, + fetched: [] + }); + + page_config = get_page_config(error_node, options); branch = branch.slice(0, j + 1).concat(error_loaded); break ssr; } catch (err) { From 4c5036c4a29fd64ebced45074739edda06083b5d Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 07:10:33 -0400 Subject: [PATCH 030/110] add placeholder setHeaders function --- packages/kit/src/runtime/server/index.js | 13 +++++++++++++ packages/kit/src/runtime/server/page/load_node.js | 3 ++- packages/kit/types/index.d.ts | 4 +++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 497a49c8c561..358253517a37 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -112,6 +112,9 @@ export async function respond(request, options, state) { } } + /** @type {import('types').ResponseHeaders} */ + const headers = {}; + /** @type {import('types').RequestEvent} */ const event = { get clientAddress() { @@ -132,6 +135,16 @@ export async function respond(request, options, state) { platform: state.platform, request, routeId: route && route.id, + setHeaders: (new_headers) => { + for (const key in new_headers) { + if (key in headers) { + throw new Error(`"${key}" header is already set`); + } + + // TODO apply these headers to the response + headers[key] = new_headers[key]; + } + }, url }; diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index 7e59ab19eb95..d9c3c4e0a713 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -304,7 +304,8 @@ export async function load_node({ event, options, state, route, node, $session } }); return proxy; - } + }, + setHeaders: event.setHeaders }; if (options.dev) { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index f5b58c933177..bbb18f50397c 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -192,8 +192,9 @@ export interface LoadEvent< data: Data; routeId: string | null; session: App.Session; + setHeaders: (headers: ResponseHeaders) => void; url: URL; - // TODO parent, setHeaders, depends + // TODO parent, depends } export interface LoadOutputCache { @@ -225,6 +226,7 @@ export interface RequestEvent = Record; request: Request; routeId: string | null; + setHeaders: (headers: ResponseHeaders) => void; url: URL; } From 139fe08aad399d53773d060dcfdbc30852062932 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 07:10:56 -0400 Subject: [PATCH 031/110] replace CSRComponent with CSRPageNode --- packages/kit/src/runtime/client/ambient.d.ts | 6 +++--- packages/kit/src/runtime/client/client.js | 14 ++++++-------- packages/kit/src/vite/build/build_server.js | 2 +- packages/kit/types/internal.d.ts | 7 +++++-- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/kit/src/runtime/client/ambient.d.ts b/packages/kit/src/runtime/client/ambient.d.ts index 5f86e2fce7ef..e82b36733888 100644 --- a/packages/kit/src/runtime/client/ambient.d.ts +++ b/packages/kit/src/runtime/client/ambient.d.ts @@ -1,10 +1,10 @@ declare module '__GENERATED__/client-manifest.js' { - import { CSRComponentLoader, ParamMatcher } from 'types'; + import { CSRPageNodeLoader, ParamMatcher } from 'types'; /** - * A list of all the layout/pages components used in the app + * A list of all the error/layout/page nodes used in the app */ - export const components: CSRComponentLoader[]; + export const nodes: CSRPageNodeLoader[]; /** * A map of `[routeId: string]: [a, b, has_endpoint]` tuples, which diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 508c1e40cae6..82a96f06a402 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -15,19 +15,17 @@ import { lock_fetch, unlock_fetch, initial_fetch, native_fetch } from './fetcher import { parse } from './parse.js'; import Root from '__GENERATED__/root.svelte'; -import { error_components, nodes, dictionary, matchers } from '__GENERATED__/client-manifest.js'; +import { nodes, dictionary, matchers } from '__GENERATED__/client-manifest.js'; const SCROLL_KEY = 'sveltekit:scroll'; const INDEX_KEY = 'sveltekit:index'; -const routes = parse(components, dictionary, matchers); +const routes = parse(nodes, dictionary, matchers); -// we import the root layout/error components eagerly, so that +// we import the root layout/error nodes eagerly, so that // connectivity errors after initialisation don't nuke the app -const default_layout = components[0](); -const default_error = components[1](); - -const root_stuff = {}; +const default_layout = nodes[0](); +const default_error = nodes[1](); // We track the scroll position associated with each history entry in sessionStorage, // rather than on history.state itself, because when navigation is driven by @@ -487,7 +485,7 @@ export function create_client({ target, session, base, trailing_slash }) { * @param {{ * status?: number; * error?: Error; - * module: import('types').CSRComponent; + * node: import('types').CSRPageNode; * url: URL; * params: Record; * props?: Record; diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 053f9cfdb52b..cf1798d9e712 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -182,7 +182,7 @@ export async function build_server(options, client) { const name = relative.startsWith('..') ? posixify(path.join('entries/fallbacks', path.basename(node.component))) - : posixify(path.join('entries/pages', relative)); + : posixify(path.join('entries/components', relative)); input[name] = resolved; } }); diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index 8445cdeb9725..e0fca120f6e9 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -1,4 +1,5 @@ import { OutputAsset, OutputChunk } from 'rollup'; +import { SvelteComponent } from 'svelte/internal'; import { Config, ExternalFetch, @@ -64,9 +65,11 @@ export interface BuildData { }; } -export type CSRComponent = any; // TODO +export interface CSRPageNode { + component: typeof SvelteComponent; +} -export type CSRComponentLoader = () => Promise; +export type CSRPageNodeLoader = () => Promise; export type CSRRoute = { id: string; From c87e77b20c65359cb9ea1e332e35e8034dfacde1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 07:23:11 -0400 Subject: [PATCH 032/110] fix build --- packages/kit/src/vite/build/build_server.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index cf1798d9e712..3c76fb7b01a8 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -176,14 +176,16 @@ export async function build_server(options, client) { // ...and every component used by pages... manifest_data.nodes.forEach((node, i) => { - if (node.component) { - const resolved = path.resolve(cwd, node.component); - const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); - - const name = relative.startsWith('..') - ? posixify(path.join('entries/fallbacks', path.basename(node.component))) - : posixify(path.join('entries/components', relative)); - input[name] = resolved; + for (const file of [node.component, node.module, node.server]) { + if (file) { + const resolved = path.resolve(cwd, file); + const relative = decodeURIComponent(path.relative(config.kit.files.routes, resolved)); + + const name = relative.startsWith('..') + ? posixify(path.join('entries/fallbacks', path.basename(file))) + : posixify(path.join('entries/pages', relative.replace(/\.js$/, ''))); + input[name] = resolved; + } } }); From 65f37a2cc28c40a72086ecab5897b7e7bc674342 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 08:01:37 -0400 Subject: [PATCH 033/110] amp tests passing! now for the client --- packages/kit/src/core/generate_manifest/index.js | 2 +- packages/kit/src/vite/build/build_server.js | 8 +++++--- packages/kit/src/vite/build/utils.js | 4 +++- .../kit/test/apps/amp/src/routes/valid.json/+server.js | 6 +++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 83e18c9bce60..e2038e513041 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -19,7 +19,7 @@ export function generate_manifest({ build_data, relative_path, routes, format = build_data.manifest_data.nodes.forEach((node, i) => { bundled_nodes.set(node, { - path: `${relative_path}/nodes/0.js`, + path: `${relative_path}/nodes/${i}.js`, index: i }); }); diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 3c76fb7b01a8..15fa6a279456 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -252,7 +252,7 @@ export async function build_server(options, client) { const entry = find_deps(client.vite_manifest, node.component, true); exports.push( - `export { default as module } from '../${vite_manifest[node.component].file}';`, + `export { default as component } from '../${vite_manifest[node.component].file}';`, `export const index = ${i};`, `export const file = '${entry.file}';`, `export const imports = ${s(entry.imports)};`, // TODO do we need to get imports from +page.js/+layout.js as well? @@ -277,11 +277,13 @@ export async function build_server(options, client) { } if (node.module) { - exports.push(`export * from '../${vite_manifest[node.module].file}';`); + imports.push(`import * as module from '../${vite_manifest[node.module].file}';`); + exports.push(`export { module };`); } if (node.server) { - exports.push(`export * from '../${vite_manifest[node.server].file}';`); + imports.push(`import * as server from '../${vite_manifest[node.server].file}';`); + exports.push(`export { server };`); } const out = `${output_dir}/server/nodes/${i}.js`; diff --git a/packages/kit/src/vite/build/utils.js b/packages/kit/src/vite/build/utils.js index 614a07b79a40..11e54a9cb620 100644 --- a/packages/kit/src/vite/build/utils.js +++ b/packages/kit/src/vite/build/utils.js @@ -102,7 +102,9 @@ export const get_default_config = function ({ config, input, ssr, outDir }) { output: { format: 'esm', entryFileNames: ssr ? '[name].js' : `${config.kit.appDir}/immutable/[name]-[hash].js`, - chunkFileNames: `${config.kit.appDir}/immutable/chunks/[name]-[hash].js`, + chunkFileNames: ssr + ? 'chunks/[name].js' + : `${config.kit.appDir}/immutable/chunks/[name]-[hash].js`, assetFileNames: `${config.kit.appDir}/immutable/assets/[name]-[hash][extname]` }, preserveEntrySignatures: 'strict' diff --git a/packages/kit/test/apps/amp/src/routes/valid.json/+server.js b/packages/kit/test/apps/amp/src/routes/valid.json/+server.js index 26b312b3a084..f81c04b42f9e 100644 --- a/packages/kit/test/apps/amp/src/routes/valid.json/+server.js +++ b/packages/kit/test/apps/amp/src/routes/valid.json/+server.js @@ -1,3 +1,7 @@ export function GET() { - return new Response(JSON.stringify({ answer: 42 })); + return new Response(JSON.stringify({ answer: 42 }), { + headers: { + 'content-type': 'application/json' + } + }); } From 73dfd28b0f0c21df9b8900e3a309d94fc7cfccbb Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 08:18:35 -0400 Subject: [PATCH 034/110] fix create_manifest_data unit tests --- .../core/sync/create_manifest_data/index.js | 2 +- .../sync/create_manifest_data/index.spec.js | 181 +++++++----------- 2 files changed, 69 insertions(+), 114 deletions(-) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index faee410b4cfe..c0092b1c5609 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -23,7 +23,7 @@ export default function create_manifest_data({ /** @type {Map} */ const route_map = new Map(); - /** @type {Map} */ const segment_map = new Map(); /** @type {import('./types').RouteTree} */ diff --git a/packages/kit/src/core/sync/create_manifest_data/index.spec.js b/packages/kit/src/core/sync/create_manifest_data/index.spec.js index d3d633b1bdb7..8dd12b23a367 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.spec.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.spec.js @@ -30,24 +30,19 @@ const default_layout = { component: 'layout.svelte' }; -const default_error = 'error.svelte'; +const default_error = { + component: 'error.svelte' +}; test('creates routes', () => { - const { components, routes } = create('samples/basic'); + const { nodes, routes } = create('samples/basic'); - const index = 'samples/basic/+page.svelte'; - const about = 'samples/basic/about/+page.svelte'; - const blog = 'samples/basic/blog/+page.svelte'; - const blog_$slug = 'samples/basic/blog/[slug]/+page.svelte'; + const index = { component: 'samples/basic/+page.svelte' }; + const about = { component: 'samples/basic/about/+page.svelte' }; + const blog = { component: 'samples/basic/blog/+page.svelte' }; + const blog_$slug = { component: 'samples/basic/blog/[slug]/+page.svelte' }; - assert.equal(components, [ - default_layout.component, - default_error, - index, - about, - blog, - blog_$slug - ]); + assert.equal(nodes, [default_layout, default_error, index, about, blog, blog_$slug]); assert.equal(routes, [ { @@ -56,9 +51,7 @@ test('creates routes', () => { pattern: /^\/$/, errors: [default_error], layouts: [default_layout], - page: { - component: index - } + page: index }, { @@ -74,9 +67,7 @@ test('creates routes', () => { pattern: /^\/about\/?$/, errors: [default_error], layouts: [default_layout], - page: { - component: about - } + page: about }, { @@ -85,9 +76,7 @@ test('creates routes', () => { pattern: /^\/blog\/?$/, errors: [default_error], layouts: [default_layout], - page: { - component: blog - } + page: blog }, { @@ -103,9 +92,7 @@ test('creates routes', () => { pattern: /^\/blog\/([^/]+?)\/?$/, errors: [default_error], layouts: [default_layout], - page: { - component: blog_$slug - } + page: blog_$slug } ]); }); @@ -117,45 +104,43 @@ const symlink_survived_git = fs const test_symlinks = symlink_survived_git ? test : test.skip; test_symlinks('creates symlinked routes', () => { - const { components, routes } = create('samples/symlinks/routes'); + const { nodes, routes } = create('samples/symlinks/routes'); - const index = 'samples/symlinks/routes/index.svelte'; - const symlinked_index = 'samples/symlinks/routes/foo/index.svelte'; + const index = { component: 'samples/symlinks/routes/index.svelte' }; + const symlinked_index = { component: 'samples/symlinks/routes/foo/index.svelte' }; - assert.equal(components, [default_layout, default_error, symlinked_index, index]); + assert.equal(nodes, [default_layout, default_error, symlinked_index, index]); assert.equal(routes, [ { type: 'page', id: '', pattern: /^\/$/, - path: '/', - shadow: null, - a: [default_layout, index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: index }, { type: 'page', id: 'foo', pattern: /^\/foo\/?$/, - path: '/foo', - shadow: null, - a: [default_layout, symlinked_index], - b: [default_error] + errors: [default_error], + layouts: [default_layout], + page: symlinked_index } ]); }); test('creates routes with layout', () => { - const { components, routes } = create('samples/basic-layout'); + const { nodes, routes } = create('samples/basic-layout'); - const layout = 'samples/basic-layout/+layout.svelte'; - const index = 'samples/basic-layout/+page.svelte'; - const foo___layout = 'samples/basic-layout/foo/+layout.svelte'; - const foo = 'samples/basic-layout/foo/+page.svelte'; + const layout = { component: 'samples/basic-layout/+layout.svelte' }; + const index = { component: 'samples/basic-layout/+page.svelte' }; + const foo___layout = { component: 'samples/basic-layout/foo/+layout.svelte' }; + const foo = { component: 'samples/basic-layout/foo/+page.svelte' }; - assert.equal(components, [layout, default_error, foo___layout, index, foo]); + assert.equal(nodes, [layout, default_error, foo___layout, index, foo]); assert.equal( routes.slice(1, 2), @@ -165,14 +150,8 @@ test('creates routes with layout', () => { id: '', pattern: /^\/$/, errors: [default_error], - layouts: [ - { - component: layout - } - ], - page: { - component: index - } + layouts: [layout], + page: index }, { @@ -180,40 +159,31 @@ test('creates routes with layout', () => { id: 'foo', pattern: /^\/foo\/?$/, errors: [default_error], - layouts: [ - { - component: layout - }, - { - component: foo___layout - } - ], - page: { - component: foo - } + layouts: [layout, foo___layout], + page: foo } ].slice(1, 2) ); }); test('succeeds when routes does not exist', () => { - const { components, routes } = create('samples/basic/routes'); - assert.equal(components, ['layout.svelte', 'error.svelte']); + const { nodes, routes } = create('samples/basic/routes'); + assert.equal(nodes, [default_layout, default_error]); assert.equal(routes, []); }); // TODO some characters will need to be URL-encoded in the filename test('encodes invalid characters', () => { - const { components, routes } = create('samples/encoding'); + const { nodes, routes } = create('samples/encoding'); // had to remove ? and " because windows // const quote = 'samples/encoding/".svelte'; - const hash = 'samples/encoding/%23/+page.svelte'; + const hash = { component: 'samples/encoding/%23/+page.svelte' }; // const question_mark = 'samples/encoding/?.svelte'; - assert.equal(components, [ - default_layout.component, + assert.equal(nodes, [ + default_layout, default_error, // quote, hash @@ -362,23 +332,16 @@ test('ignores things that look like lockfiles', () => { }); test('works with custom extensions', () => { - const { components, routes } = create('samples/custom-extension', { + const { nodes, routes } = create('samples/custom-extension', { extensions: ['.jazz', '.beebop', '.funk', '.svelte'] }); - const index = 'samples/custom-extension/+page.funk'; - const about = 'samples/custom-extension/about/+page.jazz'; - const blog = 'samples/custom-extension/blog/+page.svelte'; - const blog_$slug = 'samples/custom-extension/blog/[slug]/+page.beebop'; + const index = { component: 'samples/custom-extension/+page.funk' }; + const about = { component: 'samples/custom-extension/about/+page.jazz' }; + const blog = { component: 'samples/custom-extension/blog/+page.svelte' }; + const blog_$slug = { component: 'samples/custom-extension/blog/[slug]/+page.beebop' }; - assert.equal(components, [ - default_layout.component, - default_error, - index, - about, - blog, - blog_$slug - ]); + assert.equal(nodes, [default_layout, default_error, index, about, blog, blog_$slug]); assert.equal(routes, [ { @@ -387,9 +350,7 @@ test('works with custom extensions', () => { pattern: /^\/$/, errors: [default_error], layouts: [default_layout], - page: { - component: index - } + page: index }, { @@ -405,9 +366,7 @@ test('works with custom extensions', () => { pattern: /^\/about\/?$/, errors: [default_error], layouts: [default_layout], - page: { - component: about - } + page: about }, { @@ -416,9 +375,7 @@ test('works with custom extensions', () => { pattern: /^\/blog\/?$/, errors: [default_error], layouts: [default_layout], - page: { - component: blog - } + page: blog }, { @@ -434,9 +391,7 @@ test('works with custom extensions', () => { pattern: /^\/blog\/([^/]+?)\/?$/, errors: [default_error], layouts: [default_layout], - page: { - component: blog_$slug - } + page: blog_$slug } ]); }); @@ -469,8 +424,8 @@ test('includes nested error components', () => { errors: [ default_error, undefined, - 'samples/nested-errors/foo/bar/+error.svelte', - 'samples/nested-errors/foo/bar/baz/+error.svelte' + { component: 'samples/nested-errors/foo/bar/+error.svelte' }, + { component: 'samples/nested-errors/foo/bar/baz/+error.svelte' } ], layouts: [ default_layout, @@ -486,25 +441,25 @@ test('includes nested error components', () => { }); test('creates routes with named layouts', () => { - const { components, routes } = create('samples/named-layouts'); + const { nodes, routes } = create('samples/named-layouts'); - assert.equal(components, [ - 'samples/named-layouts/+layout.svelte', + assert.equal(nodes, [ + { component: 'samples/named-layouts/+layout.svelte' }, default_error, - 'samples/named-layouts/+layout-home@default.svelte', - 'samples/named-layouts/+layout-special.svelte', - 'samples/named-layouts/a/+layout.svelte', - 'samples/named-layouts/b/+layout-alsospecial@special.svelte', - 'samples/named-layouts/b/c/+layout.svelte', - 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte', - 'samples/named-layouts/b/d/+layout-special.svelte', - 'samples/named-layouts/a/a1/+page.svelte', - 'samples/named-layouts/a/a2/+page@special.svelte', - 'samples/named-layouts/b/c/c1/+page@alsospecial.svelte', - 'samples/named-layouts/b/c/c2/+page@home.svelte', - 'samples/named-layouts/b/d/+page@special.svelte', - 'samples/named-layouts/b/d/d1/+page.svelte', - 'samples/named-layouts/b/d/d2/+page@extraspecial.svelte' + { component: 'samples/named-layouts/+layout-home@default.svelte' }, + { component: 'samples/named-layouts/+layout-special.svelte' }, + { component: 'samples/named-layouts/a/+layout.svelte' }, + { component: 'samples/named-layouts/b/+layout-alsospecial@special.svelte' }, + { component: 'samples/named-layouts/b/c/+layout.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-extraspecial@special.svelte' }, + { component: 'samples/named-layouts/b/d/+layout-special.svelte' }, + { component: 'samples/named-layouts/a/a1/+page.svelte' }, + { component: 'samples/named-layouts/a/a2/+page@special.svelte' }, + { component: 'samples/named-layouts/b/c/c1/+page@alsospecial.svelte' }, + { component: 'samples/named-layouts/b/c/c2/+page@home.svelte' }, + { component: 'samples/named-layouts/b/d/+page@special.svelte' }, + { component: 'samples/named-layouts/b/d/d1/+page.svelte' }, + { component: 'samples/named-layouts/b/d/d2/+page@extraspecial.svelte' } ]); assert.equal(routes, [ From 49125f2ad29fdeda6ae43482629698ff8764aa53 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 10:14:19 -0400 Subject: [PATCH 035/110] add error/redirect helpers --- packages/kit/package.json | 1 + packages/kit/rollup.config.js | 132 ++++++++++++------------------ packages/kit/src/index/index.js | 17 ++++ packages/kit/src/index/private.js | 21 +++++ 4 files changed, 91 insertions(+), 80 deletions(-) create mode 100644 packages/kit/src/index/index.js create mode 100644 packages/kit/src/index/private.js diff --git a/packages/kit/package.json b/packages/kit/package.json index b22547e00afb..ece68e5e368e 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -78,6 +78,7 @@ "exports": { "./package.json": "./package.json", ".": { + "import": "./dist/index.js", "types": "./types/index.d.ts" }, "./node": { diff --git a/packages/kit/rollup.config.js b/packages/kit/rollup.config.js index 884eba331230..0783a9711dd5 100644 --- a/packages/kit/rollup.config.js +++ b/packages/kit/rollup.config.js @@ -11,87 +11,59 @@ const external = [].concat( Object.keys(pkg.peerDependencies || {}), Object.keys(process.binding('natives')), 'typescript', - 'svelte2tsx' + 'svelte2tsx', + 'svelte', + 'svelte/store', + '__GENERATED__/root.svelte', + '__GENERATED__/client-manifest.js' ); -export default [ - { - input: { - 'client/start': 'src/runtime/client/start.js', - 'client/singletons': 'src/runtime/client/singletons.js', - 'app/navigation': 'src/runtime/app/navigation.js', - 'app/stores': 'src/runtime/app/stores.js', - 'app/paths': 'src/runtime/app/paths.js', - 'app/env': 'src/runtime/app/env.js', - 'env/dynamic/private': 'src/runtime/env/dynamic/private.js', - 'env/dynamic/public': 'src/runtime/env/dynamic/public.js', - 'env-private': 'src/runtime/env-private.js', - 'env-public': 'src/runtime/env-public.js', - paths: 'src/runtime/paths.js', - env: 'src/runtime/env.js' - }, - output: { - dir: 'assets', - format: 'esm', - chunkFileNames: 'chunks/[name].js' - }, - external: [ - 'svelte', - 'svelte/store', - '__GENERATED__/root.svelte', - '__GENERATED__/client-manifest.js' - ], - plugins: [ - resolve({ - extensions: ['.mjs', '.js', '.ts'] - }) - ] - }, +export default { + input: { + // TODO move assets to dist/assets + 'assets/client/start': 'src/runtime/client/start.js', + 'assets/client/singletons': 'src/runtime/client/singletons.js', + 'assets/app/navigation': 'src/runtime/app/navigation.js', + 'assets/app/stores': 'src/runtime/app/stores.js', + 'assets/app/paths': 'src/runtime/app/paths.js', + 'assets/app/env': 'src/runtime/app/env.js', + 'assets/env/dynamic/private': 'src/runtime/env/dynamic/private.js', + 'assets/env/dynamic/public': 'src/runtime/env/dynamic/public.js', + 'assets/env-private': 'src/runtime/env-private.js', + 'assets/env-public': 'src/runtime/env-public.js', + 'assets/paths': 'src/runtime/paths.js', + 'assets/env': 'src/runtime/env.js', + 'assets/server/index': 'src/runtime/server/index.js', - { - input: 'src/runtime/server/index.js', - output: { - format: 'esm', - file: 'assets/server/index.js' - }, - plugins: [ - resolve({ - extensions: ['.mjs', '.js', '.ts'] - }), - commonjs() - ] + // TODO move dist to dist/exports + 'dist/cli': 'src/cli.js', + 'dist/index': 'src/index/index.js', + 'dist/hooks': 'src/hooks.js', + 'dist/node': 'src/node/index.js', + 'dist/node/polyfills': 'src/node/polyfills.js', + 'dist/prerender': 'src/core/prerender/prerender.js', + 'dist/vite': 'src/vite/index.js' }, - - { - input: { - cli: 'src/cli.js', - hooks: 'src/hooks.js', - node: 'src/node/index.js', - 'node/polyfills': 'src/node/polyfills.js', - prerender: 'src/core/prerender/prerender.js', - vite: 'src/vite/index.js' - }, - output: { - dir: 'dist', - format: 'esm', - chunkFileNames: 'chunks/[name].js' - }, - external: (id) => { - return id.startsWith('node:') || external.includes(id); - }, - plugins: [ - replace({ - preventAssignment: true, - values: { - __VERSION__: pkg.version, - 'process.env.BUNDLED': 'true' - } - }), - resolve({ - extensions: ['.mjs', '.js', '.ts'] - }), - commonjs() - ], - preserveEntrySignatures: true - } -]; + output: { + dir: '.', + format: 'esm', + chunkFileNames: 'dist/chunks/[name].js' + }, + external: (id) => { + return id.startsWith('node:') || external.includes(id); + }, + plugins: [ + replace({ + preventAssignment: true, + values: { + __VERSION__: pkg.version, + 'process.env.BUNDLED': 'true' + } + }), + resolve({ + extensions: ['.mjs', '.js', '.ts'] + }), + commonjs() + ], + preserveEntrySignatures: true +}; diff --git a/packages/kit/src/index/index.js b/packages/kit/src/index/index.js new file mode 100644 index 000000000000..c7a885ae8c63 --- /dev/null +++ b/packages/kit/src/index/index.js @@ -0,0 +1,17 @@ +import { HttpError, Redirect } from './private.js'; + +/** + * @param {number} status + * @param {string} message + */ +export function error(status, message) { + return new HttpError(status, message); +} + +/** + * @param {number} status + * @param {string} location + */ +export function redirect(status, location) { + return new Redirect(status, location); +} diff --git a/packages/kit/src/index/private.js b/packages/kit/src/index/private.js new file mode 100644 index 000000000000..91c74f708cc1 --- /dev/null +++ b/packages/kit/src/index/private.js @@ -0,0 +1,21 @@ +export class HttpError { + /** + * @param {number} status + * @param {string} message + */ + constructor(status, message) { + this.status = status; + this.message = message; + } +} + +export class Redirect { + /** + * @param {number} status + * @param {string} location + */ + constructor(status, location) { + this.status = status; + this.location = location; + } +} From 44749c8c54ab467dba67e04ed856fe03bf7ab690 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 10:14:57 -0400 Subject: [PATCH 036/110] rename @sveltejs/kit/data -> @sveltejs/kit --- .../templates/default/src/routes/todos/+page.server.ts | 2 +- packages/kit/test/apps/basics/src/routes/+layout.js | 2 +- .../kit/test/apps/basics/src/routes/encoded/redirect/+page.js | 2 +- .../src/routes/encoded/\345\217\215\345\272\224/+page.js" | 2 +- .../test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js | 2 +- .../src/routes/errors/endpoint-shadow-not-ok/+page.server.js | 2 +- .../kit/test/apps/basics/src/routes/errors/endpoint/+page.js | 2 +- .../apps/basics/src/routes/errors/load-error-client/+page.js | 2 +- .../src/routes/errors/load-error-malformed-client/+page.js | 2 +- .../src/routes/errors/load-error-malformed-server/+page.js | 2 +- .../apps/basics/src/routes/errors/load-error-server/+page.js | 2 +- .../basics/src/routes/errors/load-error-string-client/+page.js | 2 +- .../basics/src/routes/errors/load-error-string-server/+page.js | 2 +- .../src/routes/errors/load-status-without-error-client/+page.js | 2 +- .../src/routes/errors/load-status-without-error-server/+page.js | 2 +- .../routes/errors/page-endpoint/get-explicit/+page.server.js | 2 +- .../routes/errors/page-endpoint/post-explicit/+page.server.js | 2 +- .../test/apps/basics/src/routes/nested-layout/error/+page.js | 2 +- .../kit/test/apps/basics/src/routes/redirect-on-load/+page.js | 2 +- packages/kit/test/apps/basics/src/routes/redirect/a/+page.js | 2 +- packages/kit/test/apps/basics/src/routes/redirect/b/+page.js | 2 +- .../kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js | 2 +- .../kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js | 2 +- .../apps/basics/src/routes/redirect/missing-status/a/+page.js | 2 +- .../apps/basics/src/routes/redirect/missing-status/b/+page.js | 2 +- .../apps/basics/src/routes/shadowed/error-get/+page.server.js | 2 +- .../routes/shadowed/redirect-get-with-cookie/+page.server.js | 2 +- .../basics/src/routes/shadowed/redirect-get/+page.server.js | 2 +- .../routes/shadowed/redirect-post-with-cookie/+page.server.js | 2 +- .../basics/src/routes/shadowed/redirect-post/+page.server.js | 2 +- .../basics/src/routes/shadowed/redirect/[a]/+page.server.js | 2 +- .../kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js | 2 +- sites/kit.svelte.dev/src/routes/docs/+page.ts | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts b/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts index 9e18e79c249e..8a8caa9ba228 100644 --- a/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts +++ b/packages/create-svelte/templates/default/src/routes/todos/+page.server.ts @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; import { api } from './api'; import type { GET, POST, PATCH, DELETE } from './$types'; import type { Todo } from './types'; diff --git a/packages/kit/test/apps/basics/src/routes/+layout.js b/packages/kit/test/apps/basics/src/routes/+layout.js index fa32195cdb57..441e5aeccfd4 100644 --- a/packages/kit/test/apps/basics/src/routes/+layout.js +++ b/packages/kit/test/apps/basics/src/routes/+layout.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load({ fetch, url }) { diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js b/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js index 8d65fa6353a7..aa5d615b50e3 100644 --- a/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js +++ b/packages/kit/test/apps/basics/src/routes/encoded/redirect/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export function load() { diff --git "a/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" "b/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" index 228fc79ebf1d..9a25d2560422 100644 --- "a/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" +++ "b/packages/kit/test/apps/basics/src/routes/encoded/\345\217\215\345\272\224/+page.js" @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(307, encodeURI('苗条')); diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js index 611689855dcd..1b676e6c1946 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load({ fetch }) { diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js index 221d5acaf782..22714f2b8063 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint-shadow-not-ok/+page.server.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET() { diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js index 93c0349e864e..4f92bd4ea33d 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load({ fetch }) { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js index 76189c6f195b..a66d3abf28f5 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-client/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load() { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client/+page.js index fa7e53ddf119..ffb3d3e36b33 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-client/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; import { browser } from '$app/env'; /** @type {import('@sveltejs/kit').Load} */ diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js index 4d4b2193df5c..75c4c4c3be17 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load() { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js index 37ba2e2289ac..c6548d0f7d73 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load() { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client/+page.js index e7d95c4518ff..6bc800d84391 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-client/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; export async function load() { if (typeof window !== 'undefined') { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js index dfd3c257edec..f27960c3bdec 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-string-server/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load() { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js index 04d0af4694e5..e426984dc01d 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-client/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; export async function load() { if (typeof window !== 'undefined') { diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js index 5fbb4814ed7a..c149968ee4df 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load() { diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js index 89c608c892d0..954b12d84be8 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; import { FancyError } from '../_shared.js'; export const GET = () => { diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js index 757e04de165b..be876789f8b3 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/post-explicit/+page.server.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; import { FancyError } from '../_shared.js'; export const POST = () => { diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js b/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js index 94e1bb8269b5..9b17193d01e2 100644 --- a/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/error/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; export function load() { throw error(500, 'Error'); diff --git a/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js b/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js index 5511686cf8e8..5cf9a8a68148 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect-on-load/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; import { browser } from '$app/env'; export async function load() { diff --git a/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js index 3a42e98bae36..93aaf22b311b 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/a/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(307, './b'); diff --git a/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js index c4c41df80d1d..169eb727728a 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/b/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(307, './c'); diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js index 3a42e98bae36..93aaf22b311b 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/loopy/a/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(307, './b'); diff --git a/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js index bdd636f6e9b6..98098bf9a4ea 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/loopy/b/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(307, './a'); diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js index f42ed681a6e1..42f945943124 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/a/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(undefined, './b'); diff --git a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js index a7afda683891..5aba1d550606 100644 --- a/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js +++ b/packages/kit/test/apps/basics/src/routes/redirect/missing-status/b/+page.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function load() { throw redirect(555, './a'); diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js index 4fc26b4ce11d..723bb851134f 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/error-get/+page.server.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; export function GET() { throw error(404, undefined); diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js index c206b829646c..737dec46622e 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get-with-cookie/+page.server.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function GET({ setHeaders }) { setHeaders({ 'set-cookie': 'shadow-redirect=happy' }); diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js index 8997c0e1776a..46bb2e319ed5 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-get/+page.server.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function GET() { throw redirect(302, '/shadowed/redirected'); diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js index 5d75803c4e65..bdd7b53b5995 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post-with-cookie/+page.server.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function POST({ setHeaders }) { setHeaders({ 'set-cookie': 'shadow-redirect=happy' }); diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js index dbf002e6171c..2b8b1976028e 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect-post/+page.server.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function POST() { throw redirect(302, '/shadowed/redirected'); diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js index 8a75f89f59de..7c4e7d65f517 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/redirect/[a]/+page.server.js @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; export function GET() { throw redirect(302, '/shadowed/redirect/c'); diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js b/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js index 253a93d6da42..4b5ef327743c 100644 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js +++ b/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js @@ -1,4 +1,4 @@ -import { error } from '@sveltejs/kit/data'; +import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export function load({ params }) { diff --git a/sites/kit.svelte.dev/src/routes/docs/+page.ts b/sites/kit.svelte.dev/src/routes/docs/+page.ts index 67c7ca5966e2..5c6da2c1346e 100644 --- a/sites/kit.svelte.dev/src/routes/docs/+page.ts +++ b/sites/kit.svelte.dev/src/routes/docs/+page.ts @@ -1,4 +1,4 @@ -import { redirect } from '@sveltejs/kit/data'; +import { redirect } from '@sveltejs/kit'; /** @type {import('./$types').Load} */ export function load() { From 1c8ba5720f105b79c88d0ba464bcc00cce201a66 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 10:31:29 -0400 Subject: [PATCH 037/110] add error/redirect to @sveltejs/kit types --- packages/kit/types/index.d.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index bbb18f50397c..c566d33605a7 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -19,6 +19,7 @@ import { TrailingSlash } from './private.js'; import { SSRNodeLoader, SSRRoute, ValidatedConfig } from './internal.js'; +import { HttpError, Redirect } from '../src/index/private.js'; export interface Adapter { name: string; @@ -273,3 +274,6 @@ export interface SSRManifest { matchers: () => Promise>; }; } + +export function error(status: number, message?: string): HttpError; +export function redirect(status: number, location?: string): Redirect; From d7cc7d548ed615508272239e0d0c8dd689219704 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 10:31:35 -0400 Subject: [PATCH 038/110] fixes --- packages/kit/src/core/sync/create_manifest_data/index.js | 2 +- packages/kit/test/apps/basics/src/routes/+error.svelte | 2 +- packages/kit/test/apps/basics/src/routes/+layout.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index c0092b1c5609..c31ee4291096 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -304,7 +304,7 @@ function analyze(file, component_extensions, module_extensions) { is_page: !!match[2], is_layout: !!match[4], is_server: !!(match[1] || match[3] || match[6]), - declares_layout: match[4] + declares_layout: match[5] }; } diff --git a/packages/kit/test/apps/basics/src/routes/+error.svelte b/packages/kit/test/apps/basics/src/routes/+error.svelte index 0b401988da18..d1897f37b61c 100644 --- a/packages/kit/test/apps/basics/src/routes/+error.svelte +++ b/packages/kit/test/apps/basics/src/routes/+error.svelte @@ -6,7 +6,7 @@ Custom error page: {$page.error.message} -

{status}

+

{$page.status}

This is your custom error page saying: "{$page.error.message}"

diff --git a/packages/kit/test/apps/basics/src/routes/+layout.js b/packages/kit/test/apps/basics/src/routes/+layout.js index 441e5aeccfd4..0716988f42f4 100644 --- a/packages/kit/test/apps/basics/src/routes/+layout.js +++ b/packages/kit/test/apps/basics/src/routes/+layout.js @@ -4,7 +4,7 @@ import { error } from '@sveltejs/kit'; export async function load({ fetch, url }) { if (url.pathname.startsWith('/errors/error-in-layout')) { const res = await fetch('/errors/error-in-layout/non-existent'); - throw error(res.status, undefined); + throw error(res.status); } return { From c968420ecb92ca36b9dfb722d3cb6c0f35eab08c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 11:45:09 -0400 Subject: [PATCH 039/110] fix a slew of typescript errors --- packages/kit/src/core/sync/sync.js | 3 +- .../src/core/sync/write_client_manifest.js | 3 +- packages/kit/src/runtime/client/client.js | 329 +++++++----------- packages/kit/src/runtime/client/parse.js | 16 +- packages/kit/src/runtime/client/start.js | 2 +- packages/kit/src/runtime/client/types.d.ts | 9 +- packages/kit/src/runtime/load.js | 76 ---- packages/kit/src/runtime/server/index.js | 32 +- packages/kit/src/runtime/server/page/index.js | 16 +- .../kit/src/runtime/server/page/render.js | 21 +- .../kit/src/runtime/server/page/types.d.ts | 2 +- packages/kit/src/vite/build/build_server.js | 4 +- packages/kit/src/vite/index.js | 2 +- packages/kit/types/internal.d.ts | 29 +- 14 files changed, 186 insertions(+), 358 deletions(-) delete mode 100644 packages/kit/src/runtime/load.js diff --git a/packages/kit/src/core/sync/sync.js b/packages/kit/src/core/sync/sync.js index ffa9ee854dda..b438a82315f3 100644 --- a/packages/kit/src/core/sync/sync.js +++ b/packages/kit/src/core/sync/sync.js @@ -28,9 +28,8 @@ export function update(config) { const manifest_data = create_manifest_data({ config }); const output = path.join(config.kit.outDir, 'generated'); - const base = path.relative('.', output); - write_client_manifest(manifest_data, base, output); + write_client_manifest(manifest_data, output); write_root(manifest_data, output); write_matchers(manifest_data, output); write_types(config, manifest_data); diff --git a/packages/kit/src/core/sync/write_client_manifest.js b/packages/kit/src/core/sync/write_client_manifest.js index 23e48096e9a4..1498c4c7d8f2 100644 --- a/packages/kit/src/core/sync/write_client_manifest.js +++ b/packages/kit/src/core/sync/write_client_manifest.js @@ -6,10 +6,9 @@ import { trim, write_if_changed } from './utils.js'; * Writes the client manifest to disk. The manifest is used to power the router. It contains the * list of routes and corresponding Svelte components (i.e. pages and layouts). * @param {import('types').ManifestData} manifest_data - * @param {string} base * @param {string} output */ -export function write_client_manifest(manifest_data, base, output) { +export function write_client_manifest(manifest_data, output) { /** @type {Map} */ const node_indexes = new Map(); diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 82a96f06a402..671d8892fe9b 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -1,7 +1,6 @@ import { onMount, tick } from 'svelte'; import { writable } from 'svelte/store'; import { coalesce_to_error } from '../../utils/error.js'; -import { normalize } from '../load.js'; import { LoadURL, decode_params, normalize_path } from '../../utils/url.js'; import { create_updated_store, @@ -16,6 +15,8 @@ import { parse } from './parse.js'; import Root from '__GENERATED__/root.svelte'; import { nodes, dictionary, matchers } from '__GENERATED__/client-manifest.js'; +import { error, redirect } from '../../index/index.js'; +import { HttpError, Redirect } from '../../index/private.js'; const SCROLL_KEY = 'sveltekit:scroll'; const INDEX_KEY = 'sveltekit:index'; @@ -352,8 +353,8 @@ export function create_client({ target, session, base, trailing_slash }) { page = navigation_result.props.page; } - const leaf_node = navigation_result.state.branch[navigation_result.state.branch.length - 1]; - router_enabled = leaf_node?.module.router !== false; + const leaf_node = navigation_result.state.branch.at(-1); + router_enabled = leaf_node?.node.module.router !== false; if (callback) callback(); @@ -403,11 +404,11 @@ export function create_client({ target, session, base, trailing_slash }) { routeId }) { const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean)); - const redirect = filtered.find((f) => f.loaded?.redirect); + const redirect = filtered.find((f) => f.redirect); /** @type {import('./types').NavigationResult} */ const result = { - redirect: redirect?.loaded?.redirect, + redirect: redirect?.redirect, state: { url, params, @@ -416,15 +417,15 @@ export function create_client({ target, session, base, trailing_slash }) { session_id }, props: { - components: filtered.map((node) => node.module.default) + components: filtered.map((branch_node) => branch_node.node.component) } }; for (let i = 0; i < filtered.length; i += 1) { // Only set props if the node actually updated. This prevents needless rerenders. if (!current.branch.some((node) => node === filtered[i])) { - const loaded = filtered[i].loaded; - result.props[`props_${i}`] = loaded ? await loaded.props : null; + const data = filtered[i].data; + result.props[`props_${i}`] = { data }; } } @@ -451,69 +452,36 @@ export function create_client({ target, session, base, trailing_slash }) { print_error('query', 'searchParams'); } - const leaf = filtered[filtered.length - 1]; - const load_cache = leaf?.loaded?.cache; - - if (load_cache) { - const key = url.pathname + url.search; // omit hash - let ready = false; - - const clear = () => { - if (cache.get(key) === result) { - cache.delete(key); - } - - unsubscribe(); - clearTimeout(timeout); - }; - - const timeout = setTimeout(clear, load_cache.maxage * 1000); - - const unsubscribe = stores.session.subscribe(() => { - if (ready) clear(); - }); - - ready = true; - - cache.set(key, result); - } - return result; } /** * @param {{ - * status?: number; - * error?: Error; * node: import('types').CSRPageNode; * url: URL; * params: Record; - * props?: Record; + * data: Record | null; * routeId: string | null; * }} options + * @returns {Promise} */ - async function load_node({ status, error, module, url, params, props, routeId }) { - /** @type {import('./types').BranchNode} */ - const node = { - module, - uses: { - params: new Set(), - url: false, - session: false, - dependencies: new Set() - }, - loaded: null + async function load_node({ node, url, params, data, routeId }) { + const uses = { + params: new Set(), + url: false, + session: false, + dependencies: new Set() }; /** @param dep {string} */ - function add_dependency(dep) { + function depends(dep) { const { href } = new URL(dep, url); - node.uses.dependencies.add(href); + uses.dependencies.add(href); } - if (props) { - // shadow endpoint props means we need to mark this URL as a dependency of itself - node.uses.dependencies.add(url.href); + if (data) { + // +page.server.js data means we need to mark this URL as a dependency of itself + uses.dependencies.add(url.href); } /** @type {Record} */ @@ -521,7 +489,7 @@ export function create_client({ target, session, base, trailing_slash }) { for (const key in params) { Object.defineProperty(uses_params, key, { get() { - node.uses.params.add(key); + uses.params.add(key); return params[key]; }, enumerable: true @@ -531,18 +499,18 @@ export function create_client({ target, session, base, trailing_slash }) { const session = $session; const load_url = new LoadURL(url); - if (module.load) { + if (node.module.load) { /** @type {import('types').LoadEvent} */ const load_input = { routeId, params: uses_params, data: data || {}, get url() { - node.uses.url = true; + uses.url = true; return load_url; }, get session() { - node.uses.session = true; + uses.session = true; return session; }, // @ts-expect-error @@ -583,13 +551,14 @@ export function create_client({ target, session, base, trailing_slash }) { // we must fixup relative urls so they are resolved from the target page const normalized = new URL(requested, url).href; - add_dependency(normalized); + depends(normalized); // prerendered pages may be served from any origin, so `initial_fetch` urls shouldn't be normalized return started ? native_fetch(normalized, init) : initial_fetch(requested, init); }, - status: status ?? null, - error: error ?? null + setHeaders: () => {}, // noop + depends + // TODO parent }; if (import.meta.env.DEV) { @@ -604,22 +573,21 @@ export function create_client({ target, session, base, trailing_slash }) { if (import.meta.env.DEV) { try { lock_fetch(); - node.loaded = normalize(await module.load.call(null, load_input)); + data = await node.module.load.call(null, load_input); } finally { unlock_fetch(); } } else { - node.loaded = normalize(await module.load.call(null, load_input)); + data = await node.module.load.call(null, load_input); } - - if (node.loaded.dependencies) { - node.loaded.dependencies.forEach(add_dependency); - } - } else if (props) { - node.loaded = normalize({ props }); } - return node; + return { + node, + data: data || {}, + uses, + redirect: undefined + }; } /** @@ -636,7 +604,7 @@ export function create_client({ target, session, base, trailing_slash }) { if (cached) return cached; } - const { a, b, has_shadow } = route; + const { errors, layouts, page } = route; const changed = current.url && { url: id !== current.url.pathname + current.url.search, @@ -645,46 +613,42 @@ export function create_client({ target, session, base, trailing_slash }) { }; /** @type {Array} */ - let branch = []; - - /** @type {number} */ - let status = 200; - - /** @type {Error | null} */ - let error = null; + const branch = []; // preload modules to avoid waterfall, but handle rejections // so they don't get reported to Sentry et al (we don't need // to act on the failures at this point) - a.forEach((loader) => loader().catch(() => {})); + [...errors, ...layouts, page].forEach((loader) => loader().catch(() => {})); + + const nodes = [...layouts, page]; - load: for (let i = 0; i < a.length; i += 1) { + for (let i = 0; i < nodes.length; i += 1) { /** @type {import('./types').BranchNode | undefined} */ - let node; + let branch_node; try { - if (!a[i]) continue; + if (!nodes[i]) continue; - const module = await a[i](); + const node = await nodes[i](); const previous = current.branch[i]; const changed_since_last_render = !previous || - module !== previous.module || + node !== previous.node || (changed.url && previous.uses.url) || changed.params.some((param) => previous.uses.params.has(param)) || (changed.session && previous.uses.session) || Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))); if (changed_since_last_render) { - /** @type {Record} */ - let props = {}; - - const is_shadow_page = has_shadow && i === a.length - 1; + /** @type {Record | null} */ + let data = null; - if (is_shadow_page) { + if (node.server) { + const url = TODO; const res = await native_fetch( - `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`, + url, + // `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`, { headers: { 'x-sveltekit-load': 'true' @@ -693,93 +657,81 @@ export function create_client({ target, session, base, trailing_slash }) { ); if (res.ok) { - const redirect = res.headers.get('x-sveltekit-location'); - - if (redirect) { - return { - redirect, - props: {}, - state: current - }; + const location = /** @type {string} */ (res.headers.get('x-sveltekit-location')); + const status = /** @type {string} */ (res.headers.get('x-sveltekit-status')); + + if (location) { + throw redirect(+status, location); } - props = res.status === 204 ? {} : await res.json(); + // TODO detect absence of `GET` earlier, so `node.server === false` and + // we don't need to much around with 204s + data = res.status === 204 ? {} : await res.json(); } else { - status = res.status; try { - error = await res.json(); + // TODO differentiate between intentional errors (thrown with `error(404)` on the server) + // and unexpected ones + const error_object = await res.json(); + throw error(res.status, error_object.message); } catch (e) { - error = new Error('Failed to load data'); + throw new Error('Failed to load data'); } } } - if (!error) { - node = await load_node({ - module, - url, - params, - props, - routeId: route.id - }); - } - - if (node) { - if (is_shadow_page) { - node.uses.url = true; - } - - if (node.loaded) { - if (node.loaded.error) { - status = node.loaded.status ?? 500; - error = node.loaded.error; - } + branch_node = await load_node({ + node, + url, + params, + data, + routeId: route.id + }); - if (node.loaded.redirect) { - return { - redirect: node.loaded.redirect, - props: {}, - state: current - }; - } - } + if (node.server) { + branch_node.uses.url = true; } } else { - node = previous; + branch_node = previous; } + + branch.push(branch_node); } catch (e) { - status = 500; - error = coalesce_to_error(e); - } + if (e instanceof Redirect) { + // TODO handle redirect + } + + const status = e instanceof HttpError ? e.status : 500; + const error = coalesce_to_error(e); - if (error) { while (i--) { - if (b[i]) { + if (errors[i]) { + /** @type {import('./types').BranchNode | undefined} */ let error_loaded; - /** @type {import('./types').BranchNode | undefined} */ - let node_loaded; let j = i; - while (!(node_loaded = branch[j])) { - j -= 1; - } + while (!branch[j]) j -= 1; try { - error_loaded = await load_node({ - status, - error, - module: await b[i](), + error_loaded = { + node: await errors[i](), + data: {}, + uses: { + params: new Set(), + url: false, + session: false, + dependencies: new Set() + }, + redirect: undefined + }; + + return await get_navigation_result_from_branch({ url, params, + branch: branch.slice(0, j + 1).concat(error_loaded), + status, + error, routeId: route.id }); - - if (error_loaded?.loaded?.error) { - continue; - } - - branch = branch.slice(0, j + 1).concat(error_loaded); - break load; } catch (e) { continue; } @@ -792,8 +744,6 @@ export function create_client({ target, session, base, trailing_slash }) { url, routeId: route.id }); - } else { - branch.push(node); } } @@ -801,8 +751,8 @@ export function create_client({ target, session, base, trailing_slash }) { url, params, branch, - status, - error, + status: 200, + error: null, routeId: route.id }); } @@ -820,16 +770,16 @@ export function create_client({ target, session, base, trailing_slash }) { const params = {}; // error page does not have params const root_layout = await load_node({ - module: await default_layout, + data: null, + node: await default_layout, url, params, routeId }); const root_error = await load_node({ - status, - error, - module: await default_error, + data: null, + node: await default_error, url, params, routeId @@ -1008,7 +958,9 @@ export function create_client({ target, session, base, trailing_slash }) { ? routes.filter((route) => pathnames.some((pathname) => route.exec(pathname))) : routes; - const promises = matching.map((r) => Promise.all(r.a.map((load) => load()))); + const promises = matching.map((r) => { + Promise.all([...r.errors, ...r.layouts, r.page].map((load) => load())); + }); await Promise.all(promises); }, @@ -1204,7 +1156,7 @@ export function create_client({ target, session, base, trailing_slash }) { }); }, - _hydrate: async ({ status, error, nodes, params, routeId }) => { + _hydrate: async ({ status, error, node_ids, params, routeId }) => { const url = new URL(location.href); /** @type {Array} */ @@ -1213,62 +1165,45 @@ export function create_client({ target, session, base, trailing_slash }) { /** @type {import('./types').NavigationResult | undefined} */ let result; - let error_args; - try { for (let i = 0; i < nodes.length; i += 1) { const is_leaf = i === nodes.length - 1; - let props; + let data; if (is_leaf) { const serialized = document.querySelector('script[sveltekit\\:data-type="props"]'); if (serialized) { - props = JSON.parse(/** @type {string} */ (serialized.textContent)); + data = JSON.parse(/** @type {string} */ (serialized.textContent)); } } - const node = await load_node({ - module: await components[nodes[i]](), + const branch_node = await load_node({ + node: await nodes[node_ids[i]](), url, params, - status: is_leaf ? status : undefined, - error: is_leaf ? error : undefined, - props, + data, routeId }); - if (props) { - node.uses.dependencies.add(url.href); - node.uses.url = true; + if (data) { + branch_node.uses.dependencies.add(url.href); + branch_node.uses.url = true; } - branch.push(node); - - if (node && node.loaded) { - if (node.loaded.error) { - if (error) throw node.loaded.error; - error_args = { - status: node.loaded.status ?? 500, - error: node.loaded.error, - url, - routeId - }; - } - } + branch.push(branch_node); } - result = error_args - ? await load_root_error_page(error_args) - : await get_navigation_result_from_branch({ - url, - params, - branch, - status, - error, - routeId - }); + result = await get_navigation_result_from_branch({ + url, + params, + branch, + status, + error, + routeId + }); } catch (e) { + // TODO handle HttpError and Redirect cases if (error) throw e; result = await load_root_error_page({ diff --git a/packages/kit/src/runtime/client/parse.js b/packages/kit/src/runtime/client/parse.js index 294021924293..dcb225b47ae3 100644 --- a/packages/kit/src/runtime/client/parse.js +++ b/packages/kit/src/runtime/client/parse.js @@ -1,13 +1,13 @@ import { exec, parse_route_id } from '../../utils/routing.js'; /** - * @param {import('types').CSRComponentLoader[]} components - * @param {Record} dictionary + * @param {import('types').CSRPageNodeLoader[]} nodes + * @param {Record} dictionary * @param {Record boolean>} matchers * @returns {import('types').CSRRoute[]} */ -export function parse(components, dictionary, matchers) { - const routes = Object.entries(dictionary).map(([id, [a, b, has_shadow]]) => { +export function parse(nodes, dictionary, matchers) { + return Object.entries(dictionary).map(([id, [errors, layouts, page]]) => { const { pattern, names, types } = parse_route_id(id); return { @@ -17,11 +17,9 @@ export function parse(components, dictionary, matchers) { const match = pattern.exec(path); if (match) return exec(match, names, types, matchers); }, - a: a.map((n) => components[n]), - b: b.map((n) => components[n]), - has_shadow: !!has_shadow + errors: errors.map((n) => nodes[n]), + layouts: layouts.map((n) => nodes[n]), + page: nodes[page] }; }); - - return routes; } diff --git a/packages/kit/src/runtime/client/start.js b/packages/kit/src/runtime/client/start.js index fcba7c748bd6..61c5645e02b1 100644 --- a/packages/kit/src/runtime/client/start.js +++ b/packages/kit/src/runtime/client/start.js @@ -18,7 +18,7 @@ export { set_public_env } from '../env-public.js'; * hydrate: { * status: number; * error: Error; - * nodes: number[]; + * node_ids: number[]; * params: Record; * routeId: string | null; * }; diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index 68e4ac48a4a1..5be69f75f66a 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -6,7 +6,7 @@ import { prefetch, prefetchRoutes } from '$app/navigation'; -import { CSRComponent, CSRRoute, NormalizedLoadOutput } from 'types'; +import { CSRPageNode, CSRRoute } from 'types'; export interface Client { // public API, exposed via $app/navigation @@ -22,7 +22,7 @@ export interface Client { _hydrate: (opts: { status: number; error: Error; - nodes: number[]; + node_ids: number[]; params: Record; routeId: string | null; }) => Promise; @@ -55,14 +55,15 @@ export type NavigationResult = { }; export type BranchNode = { - module: CSRComponent; - loaded: NormalizedLoadOutput | null; + node: CSRPageNode; + data: Record; uses: { params: Set; url: boolean; // TODO make more granular? session: boolean; dependencies: Set; }; + redirect: string | undefined; }; export type NavigationState = { diff --git a/packages/kit/src/runtime/load.js b/packages/kit/src/runtime/load.js deleted file mode 100644 index d1ab2b863ceb..000000000000 --- a/packages/kit/src/runtime/load.js +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @param {import('types').LoadOutput | void} loaded - * @returns {import('types').NormalizedLoadOutput} - */ -export function normalize(loaded) { - if (!loaded) { - return {}; - } - - // TODO remove for 1.0 - // @ts-expect-error - if (loaded.fallthrough) { - throw new Error( - 'fallthrough is no longer supported. Use matchers instead: https://kit.svelte.dev/docs/routing#advanced-routing-matching' - ); - } - - // TODO remove for 1.0 - if ('maxage' in loaded) { - throw new Error('maxage should be replaced with cache: { maxage }'); - } - - const has_error_status = - loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect; - if (loaded.error || has_error_status) { - const status = loaded.status; - - if (!loaded.error && has_error_status) { - return { - status: status || 500, - error: new Error(`${status}`) - }; - } - - const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error; - - if (!(error instanceof Error)) { - return { - status: 500, - error: new Error( - `"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"` - ) - }; - } - - if (!status || status < 400 || status > 599) { - console.warn('"error" returned from load() without a valid status code — defaulting to 500'); - return { status: 500, error }; - } - - return { status, error }; - } - - if (loaded.redirect) { - if (!loaded.status || Math.floor(loaded.status / 100) !== 3) { - throw new Error( - '"redirect" property returned from load() must be accompanied by a 3xx status code' - ); - } - - if (typeof loaded.redirect !== 'string') { - throw new Error('"redirect" property returned from load() must be a string'); - } - } - - if (loaded.dependencies) { - if ( - !Array.isArray(loaded.dependencies) || - loaded.dependencies.some((dep) => typeof dep !== 'string') - ) { - throw new Error('"dependencies" property returned from load() must be of type string[]'); - } - } - - return /** @type {import('types').NormalizedLoadOutput} */ (loaded); -} diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 358253517a37..bd05f186ca03 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -233,22 +233,22 @@ export async function respond(request, options, state) { throw new Error('TODO return JSON'); // loading data for a client-side transition is a special case - if (request.headers.has('x-sveltekit-load')) { - // since redirects are opaque to the browser, we need to repackage - // 3xx responses as 200s with a custom header - if (response.status >= 300 && response.status < 400) { - const location = response.headers.get('location'); - - if (location) { - const headers = new Headers(response.headers); - headers.set('x-sveltekit-location', location); - response = new Response(undefined, { - status: 204, - headers - }); - } - } - } + // if (request.headers.has('x-sveltekit-load')) { + // // since redirects are opaque to the browser, we need to repackage + // // 3xx responses as 200s with a custom header + // if (response.status >= 300 && response.status < 400) { + // const location = response.headers.get('location'); + + // if (location) { + // const headers = new Headers(response.headers); + // headers.set('x-sveltekit-location', location); + // response = new Response(undefined, { + // status: 204, + // headers + // }); + // } + // } + // } } else { response = route.type === 'endpoint' diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 492a02a8c682..028227d1e108 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -72,7 +72,7 @@ export async function render_page(event, route, options, state, resolve_opts) { // if the page isn't marked as prerenderable (or is explicitly // marked NOT prerenderable, if `prerender.default` is `true`), // then bail out at this point - const should_prerender = leaf_node.prerender ?? options.prerender.default; + const should_prerender = leaf_node.module.prerender ?? options.prerender.default; if (!should_prerender) { return new Response(undefined, { status: 204 @@ -125,12 +125,8 @@ export async function render_page(event, route, options, state, resolve_opts) { const index = /** @type {number} */ (route.errors[i]); const error_node = await options.manifest._.nodes[index](); - /** @type {Loaded} */ - let node_loaded; let j = i; - while (!(node_loaded = branch[j])) { - j -= 1; - } + while (!branch[j]) j -= 1; try { const error_loaded = /** @type {import('./types').Loaded} */ ({ @@ -203,15 +199,15 @@ export async function render_page(event, route, options, state, resolve_opts) { * @param {SSROptions} options */ function get_page_config(leaf, options) { - // TODO remove for 1.0 - if ('ssr' in leaf) { + // TODO we can reinstate this now that it's in the module + if ('ssr' in leaf.module) { throw new Error( '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle' ); } return { - router: 'router' in leaf ? !!leaf.router : options.router, - hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate + router: 'router' in leaf.module ? !!leaf.module.router : options.router, + hydrate: 'hydrate' in leaf.module ? !!leaf.module.hydrate : options.hydrate }; } diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 622da09b3889..756092bdf31a 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -64,10 +64,6 @@ export async function render_response({ let rendered; - let is_private = false; - /** @type {import('types').NormalizedLoadOutputCache | undefined} */ - let cache; - const stack = error?.stack; if (options.dev && error) { @@ -75,9 +71,7 @@ export async function render_response({ } if (resolve_opts.ssr) { - const leaf = /** @type {import('./types.js').Loaded} */ (branch.at(-1)); - - for (const { node, data, fetched } of branch) { + for (const { node, fetched } of branch) { if (node.imports) { node.imports.forEach((url) => modulepreloads.add(url)); } @@ -95,8 +89,6 @@ export async function render_response({ } const session = writable($session); - // Even if $session isn't accessed, it still ends up serialized in the rendered HTML - is_private = is_private || (cache?.private ?? (!!$session && Object.keys($session).length > 0)); /** @type {Record} */ const props = { @@ -172,7 +164,7 @@ export async function render_response({ hydrate: ${resolve_opts.ssr && page_config.hydrate ? `{ status: ${status}, error: ${error && serialize_error(error, e => e.stack)}, - nodes: [${branch.map(({ node }) => node.index).join(', ')}], + node_ids: [${branch.map(({ node }) => node.index).join(', ')}], params: ${devalue(event.params)}, routeId: ${s(event.routeId)} }` : 'null'} @@ -257,6 +249,7 @@ export async function render_response({ } if (state.prerendering) { + // TODO read headers set with setHeaders and convert into http-equiv where possible const http_equiv = []; const csp_headers = csp.csp_provider.get_meta(); @@ -264,10 +257,6 @@ export async function render_response({ http_equiv.push(csp_headers); } - if (cache) { - http_equiv.push(``); - } - if (http_equiv.length > 0) { head = http_equiv.join('\n') + head; } @@ -289,10 +278,6 @@ export async function render_response({ etag: `"${hash(html)}"` }); - if (cache) { - headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${cache.maxage}`); - } - if (!state.prerendering) { const csp_header = csp.csp_provider.get_header(); if (csp_header) { diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 09b47d0c971c..8f58706e8e0c 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -1,4 +1,4 @@ -import { JSONValue, NormalizedLoadOutput, ResponseHeaders, SSRNode, CspDirectives } from 'types'; +import { JSONValue, ResponseHeaders, SSRNode, CspDirectives } from 'types'; export type Fetched = { url: string; diff --git a/packages/kit/src/vite/build/build_server.js b/packages/kit/src/vite/build/build_server.js index 15fa6a279456..aa901f76ebb4 100644 --- a/packages/kit/src/vite/build/build_server.js +++ b/packages/kit/src/vite/build/build_server.js @@ -175,7 +175,7 @@ export async function build_server(options, client) { }); // ...and every component used by pages... - manifest_data.nodes.forEach((node, i) => { + manifest_data.nodes.forEach((node) => { for (const file of [node.component, node.module, node.server]) { if (file) { const resolved = path.resolve(cwd, file); @@ -314,7 +314,7 @@ function get_methods(cwd, output, manifest_data) { /** @type {Record} */ const methods = {}; manifest_data.routes.forEach((route) => { - const file = route.type === 'endpoint' ? route.file : route.shadow; + const file = route.type === 'endpoint' ? route.file : route.page.server; if (file && lookup[file]) { methods[file] = lookup[file].filter(is_http_method); diff --git a/packages/kit/src/vite/index.js b/packages/kit/src/vite/index.js index 224df144fd6a..4182abec6b70 100644 --- a/packages/kit/src/vite/index.js +++ b/packages/kit/src/vite/index.js @@ -283,7 +283,7 @@ function kit() { * then use this hook to kick off builds for the server and service worker. */ async writeBundle(_options, bundle) { - manifest_data.nodes.forEach((node, i) => { + manifest_data.nodes.forEach((_node, i) => { const id = vite.normalizePath( path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) ); diff --git a/packages/kit/types/internal.d.ts b/packages/kit/types/internal.d.ts index e0fca120f6e9..cc3418b05ae9 100644 --- a/packages/kit/types/internal.d.ts +++ b/packages/kit/types/internal.d.ts @@ -67,6 +67,12 @@ export interface BuildData { export interface CSRPageNode { component: typeof SvelteComponent; + module: { + load: Load; + hydrate: boolean; + router: boolean; + }; + server: boolean; } export type CSRPageNodeLoader = () => Promise; @@ -74,9 +80,9 @@ export type CSRPageNodeLoader = () => Promise; export type CSRRoute = { id: string; exec: (path: string) => undefined | Record; - a: CSRComponentLoader[]; - b: CSRComponentLoader[]; - has_shadow: boolean; + errors: CSRPageNodeLoader[]; + layouts: CSRPageNodeLoader[]; + page: CSRPageNodeLoader; }; export interface EndpointData { @@ -122,23 +128,8 @@ export interface MethodOverride { allowed: string[]; } -export type NormalizedLoadOutput = { - status?: number; - error?: Error; - redirect?: string; - props?: Record | Promise>; - stuff?: Record; - cache?: NormalizedLoadOutputCache; - dependencies?: string[]; -}; - -export interface NormalizedLoadOutputCache { - maxage: number; - private?: boolean; -} - export interface PageNode { - component: string; // TODO supply default component if it's missing (bit of an edge case) + component?: string; // TODO supply default component if it's missing (bit of an edge case) module?: string; server?: string; } From 582e54fe586451652523bc1819c96eb7f0f66b0f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 12:10:31 -0400 Subject: [PATCH 040/110] data loading belongs in load_node --- .../src/core/sync/write_client_manifest.js | 14 ++- packages/kit/src/runtime/client/client.js | 86 ++++--------------- 2 files changed, 29 insertions(+), 71 deletions(-) diff --git a/packages/kit/src/core/sync/write_client_manifest.js b/packages/kit/src/core/sync/write_client_manifest.js index 1498c4c7d8f2..0854ae64f910 100644 --- a/packages/kit/src/core/sync/write_client_manifest.js +++ b/packages/kit/src/core/sync/write_client_manifest.js @@ -12,12 +12,18 @@ export function write_client_manifest(manifest_data, output) { /** @type {Map} */ const node_indexes = new Map(); - /** @param {import('types').PageNode} node */ + /** + * Creates a module that exports a `CSRPageNode` + * @param {import('types').PageNode} node + */ function generate_node(node) { const declarations = []; if (node.module) { - declarations.push(`export * from ${s(relative(`${output}/nodes`, node.module))};`); + declarations.push( + `import * as module from ${s(relative(`${output}/nodes`, node.module))};`, + `export { module };` + ); } if (node.component) { @@ -26,6 +32,10 @@ export function write_client_manifest(manifest_data, output) { ); } + if (node.server) { + declarations.push(`export const server = true;`); + } + return declarations.join('\n'); } diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 671d8892fe9b..995fdfdb9424 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -460,12 +460,13 @@ export function create_client({ target, session, base, trailing_slash }) { * node: import('types').CSRPageNode; * url: URL; * params: Record; - * data: Record | null; * routeId: string | null; * }} options * @returns {Promise} */ - async function load_node({ node, url, params, data, routeId }) { + async function load_node({ node, url, params, routeId }) { + // TODO loading from node.server belongs in here, not in load_route etc + const uses = { params: new Set(), url: false, @@ -479,9 +480,16 @@ export function create_client({ target, session, base, trailing_slash }) { uses.dependencies.add(href); } - if (data) { - // +page.server.js data means we need to mark this URL as a dependency of itself + /** @type {Record | null} */ + let data = null; + + if (node.server) { + // +page.server.js data means we need to mark this URL as a dependency of itself, + // unless we want to get clever with usage detection on the server, which could + // be returned to the client either as payload or custom headers uses.dependencies.add(url.href); + + throw new Error('TODO load data from server'); } /** @type {Record} */ @@ -499,12 +507,12 @@ export function create_client({ target, session, base, trailing_slash }) { const session = $session; const load_url = new LoadURL(url); - if (node.module.load) { + if (node.module?.load) { /** @type {import('types').LoadEvent} */ const load_input = { routeId, params: uses_params, - data: data || {}, + data, get url() { uses.url = true; return load_url; @@ -641,55 +649,12 @@ export function create_client({ target, session, base, trailing_slash }) { Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))); if (changed_since_last_render) { - /** @type {Record | null} */ - let data = null; - - if (node.server) { - const url = TODO; - const res = await native_fetch( - url, - // `${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}__data.json${url.search}`, - { - headers: { - 'x-sveltekit-load': 'true' - } - } - ); - - if (res.ok) { - const location = /** @type {string} */ (res.headers.get('x-sveltekit-location')); - const status = /** @type {string} */ (res.headers.get('x-sveltekit-status')); - - if (location) { - throw redirect(+status, location); - } - - // TODO detect absence of `GET` earlier, so `node.server === false` and - // we don't need to much around with 204s - data = res.status === 204 ? {} : await res.json(); - } else { - try { - // TODO differentiate between intentional errors (thrown with `error(404)` on the server) - // and unexpected ones - const error_object = await res.json(); - throw error(res.status, error_object.message); - } catch (e) { - throw new Error('Failed to load data'); - } - } - } - branch_node = await load_node({ node, url, params, - data, routeId: route.id }); - - if (node.server) { - branch_node.uses.url = true; - } } else { branch_node = previous; } @@ -770,7 +735,6 @@ export function create_client({ target, session, base, trailing_slash }) { const params = {}; // error page does not have params const root_layout = await load_node({ - data: null, node: await default_layout, url, params, @@ -778,7 +742,6 @@ export function create_client({ target, session, base, trailing_slash }) { }); const root_error = await load_node({ - data: null, node: await default_error, url, params, @@ -1166,31 +1129,16 @@ export function create_client({ target, session, base, trailing_slash }) { let result; try { - for (let i = 0; i < nodes.length; i += 1) { - const is_leaf = i === nodes.length - 1; - - let data; - - if (is_leaf) { - const serialized = document.querySelector('script[sveltekit\\:data-type="props"]'); - if (serialized) { - data = JSON.parse(/** @type {string} */ (serialized.textContent)); - } - } + for (let i = 0; i < node_ids.length; i += 1) { + const node_id = node_ids[i]; const branch_node = await load_node({ - node: await nodes[node_ids[i]](), + node: await nodes[node_id](), url, params, - data, routeId }); - if (data) { - branch_node.uses.dependencies.add(url.href); - branch_node.uses.url = true; - } - branch.push(branch_node); } From 7cbe76344f58837321e7b825f865a6b87407e6ae Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 12:32:31 -0400 Subject: [PATCH 041/110] holy shit the tests are starting to pass --- packages/kit/src/runtime/client/client.js | 4 ++-- packages/kit/src/runtime/server/page/index.js | 6 +++--- .../basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte | 2 +- .../test/apps/basics/src/routes/store/stuff/foo/+page.js | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 995fdfdb9424..ac700963338a 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -354,7 +354,7 @@ export function create_client({ target, session, base, trailing_slash }) { } const leaf_node = navigation_result.state.branch.at(-1); - router_enabled = leaf_node?.node.module.router !== false; + router_enabled = leaf_node?.node.module?.router !== false; if (callback) callback(); @@ -626,7 +626,7 @@ export function create_client({ target, session, base, trailing_slash }) { // preload modules to avoid waterfall, but handle rejections // so they don't get reported to Sentry et al (we don't need // to act on the failures at this point) - [...errors, ...layouts, page].forEach((loader) => loader().catch(() => {})); + [...errors, ...layouts, page].forEach((loader) => loader?.().catch(() => {})); const nodes = [...layouts, page]; diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 028227d1e108..5b5f06e54f59 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -200,14 +200,14 @@ export async function render_page(event, route, options, state, resolve_opts) { */ function get_page_config(leaf, options) { // TODO we can reinstate this now that it's in the module - if ('ssr' in leaf.module) { + if (leaf.module && 'ssr' in leaf.module) { throw new Error( '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs/hooks#handle' ); } return { - router: 'router' in leaf.module ? !!leaf.module.router : options.router, - hydrate: 'hydrate' in leaf.module ? !!leaf.module.hydrate : options.hydrate + router: leaf.module?.router ?? options.router, + hydrate: leaf.module?.hydrate ?? options.hydrate }; } diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte index adb4129b19aa..22583491d83f 100644 --- a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte @@ -4,4 +4,4 @@

message: {data.message}

-
{JSON.stringify({ data.x, data.y, data.z })}
+
{JSON.stringify(data)}
diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.js b/packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.js index 5e2986302cfc..9a1976ddf63d 100644 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.js +++ b/packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.js @@ -12,7 +12,7 @@ export function load({ url }) { } return { - foo: true - number: 2 + foo: true, + number: 2 }; } From c4e081bbc4c7945e1c7de173af02af3dac6989b8 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 12:45:50 -0400 Subject: [PATCH 042/110] update some tests --- .../src/routes/encoded/redirected/+page.svelte | 2 +- .../load/dynamic-import-styles/+page.svelte | 3 +-- .../apps/basics/src/routes/query/echo/+page.js | 15 +-------------- .../basics/src/routes/query/echo/+page.svelte | 1 + .../apps/basics/src/routes/query/echo/utils.js | 14 ++++++++++++++ .../test/apps/basics/src/routes/xss/+page.svelte | 2 +- .../apps/basics/src/routes/xss/query/+page.js | 15 +-------------- .../apps/basics/src/routes/xss/query/+page.svelte | 1 + .../apps/basics/src/routes/xss/query/utils.js | 14 ++++++++++++++ 9 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 packages/kit/test/apps/basics/src/routes/query/echo/utils.js create mode 100644 packages/kit/test/apps/basics/src/routes/xss/query/utils.js diff --git a/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte b/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte index ac072bdc65cf..f084fc4b96b0 100644 --- a/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/encoded/redirected/+page.svelte @@ -1,6 +1,6 @@
{data.embedded}
diff --git a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte index 5aa56be5ef0e..d2a75179b0b2 100644 --- a/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/load/dynamic-import-styles/+page.svelte @@ -1,6 +1,5 @@ - + diff --git a/packages/kit/test/apps/basics/src/routes/query/echo/+page.js b/packages/kit/test/apps/basics/src/routes/query/echo/+page.js index bd73fc6f3ac8..49fb18bd3229 100644 --- a/packages/kit/test/apps/basics/src/routes/query/echo/+page.js +++ b/packages/kit/test/apps/basics/src/routes/query/echo/+page.js @@ -1,17 +1,4 @@ -/** @typedef {Record} Query */ - -/** @param {URLSearchParams} query */ -function to_pojo(query) { - /** @type {Query}*/ - const values = {}; - - query.forEach((value, key) => { - if (!(key in values)) values[key] = []; - values[key].push(value); - }); - - return values; -} +import { to_pojo } from './utils.js'; /** @type {import('@sveltejs/kit').Load} */ export function load({ url }) { diff --git a/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte b/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte index bbd45b406662..fce965f153f9 100644 --- a/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/query/echo/+page.svelte @@ -1,5 +1,6 @@

user.name is {data.user.name}

diff --git a/packages/kit/test/apps/basics/src/routes/xss/query/+page.js b/packages/kit/test/apps/basics/src/routes/xss/query/+page.js index bd73fc6f3ac8..49fb18bd3229 100644 --- a/packages/kit/test/apps/basics/src/routes/xss/query/+page.js +++ b/packages/kit/test/apps/basics/src/routes/xss/query/+page.js @@ -1,17 +1,4 @@ -/** @typedef {Record} Query */ - -/** @param {URLSearchParams} query */ -function to_pojo(query) { - /** @type {Query}*/ - const values = {}; - - query.forEach((value, key) => { - if (!(key in values)) values[key] = []; - values[key].push(value); - }); - - return values; -} +import { to_pojo } from './utils.js'; /** @type {import('@sveltejs/kit').Load} */ export function load({ url }) { diff --git a/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte b/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte index 9338a442fcaf..3df937380a09 100644 --- a/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/xss/query/+page.svelte @@ -1,5 +1,6 @@ From e75c68a6287063ce9a36dfb69db106abff35b192 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 22:34:29 -0400 Subject: [PATCH 046/110] i think it basically works --- packages/kit/src/core/sync/write_types.js | 76 +++++++++++++++++++---- packages/kit/types/index.d.ts | 21 +++++++ 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index 1995d67d6ba4..16ea0a1e2a92 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -6,8 +6,12 @@ import { rimraf } from '../../utils/filesystem.js'; import { parse_route_id } from '../../utils/routing.js'; import { write } from './utils.js'; +const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; + const module_names = new Set(['load']); -const server_names = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']); +const server_names = new Set(methods); + +const page_exports = ['Load', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']; /** * @param {import('types').ValidatedConfig} config @@ -28,12 +32,14 @@ export function write_types(config, manifest_data) { const params = parse_route_id(route.id).names; declarations.push( - `interface Params ${ + `interface Params extends Record ${ params.length > 0 ? `{ ${params.map((param) => `${param}: string`).join('; ')} }` : '{}' }` ); if (route.type === 'page') { + imports.push(`import type {\n\t${['Load', ...methods].map(name => `${name} as Generic${name}`).join(',\n\t')}\n} from '@sveltejs/kit';`); + for (const node of route.layouts) { // TODO handle edge case where a layout doesn't have a sibling +page } @@ -42,24 +48,36 @@ export function write_types(config, manifest_data) { const content = fs.readFileSync(route.page.module, 'utf8'); const proxy = tweak_types(content, module_names); - if (proxy) { - // TODO only do this if the module does in fact export `load` - write(`${outdir}/proxy${path.basename(route.page.module)}`, proxy); - imports.push(`import { load } from './proxy+page.js';`); + if (proxy && proxy.exports.includes('load')) { + const basename = path.basename(route.page.module); + write(`${outdir}/proxy${basename}`, proxy.code); + imports.push(`import { load } from './proxy${basename}';`); exports.push(`export type Data = Awaited>;`); } else { - // TODO bail out write file with `any` types + exports.push(`export type Data = any;`); } } if (route.page.server) { - // TODO + const content = fs.readFileSync(route.page.server, 'utf8'); + const proxy = tweak_types(content, server_names); + + if (proxy && proxy.exports.includes('GET')) { // TODO handle validation errors from POST/PUT/PATCH + const basename = path.basename(route.page.server); + write(`${outdir}/proxy${basename}`, proxy.code); + imports.push(`import { GET } from './proxy${basename}';`); + declarations.push(`type ServerData = Awaited>;`); + } else { + declarations.push(`type ServerData = any;`); + } + } else { + declarations.push(`type ServerData = null;`); } - // TODO write Load, GET, etc - imports.push(`import type { Load as GenericLoad, GET as GenericGET } from '@sveltejs/kit';`); - exports.push(`export type Load = GenericLoad;`); - exports.push(`export type GET = GenericGET;`); + exports.push( + `export type Load = GenericLoad;`, + ...methods.map(name => `export type ${name} = Generic${name};`) + ); } else { imports.push(`import type { RequestHandler as GenericRequestHandler } from '@sveltejs/kit';`); exports.push(`export type RequestHandler = GenericRequestHandler;`); @@ -87,6 +105,35 @@ function tweak_types(content, names) { const code = new MagicString(content); + const exports = new Map(); + + ast.forEachChild(node => { + if (ts.isExportDeclaration(node) && ts.isNamedExports(node.exportClause)) { + node.exportClause.elements.forEach(element => { + const exported = element.name; + if (names.has(element.name.text)) { + const local = element.propertyName || element.name; + exports.set(exported.text, local.text); + } + }); + } + + // 93 because there's no ts.isExportKeyword function for some reason + if (node.modifiers?.some(modifier => modifier.kind === 93)) { + if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) { + exports.set(node.name.text, node.name.text); + } + + if (ts.isVariableStatement(node)) { + node.declarationList.declarations.forEach(declaration => { + if (ts.isIdentifier(declaration.name) && names.has(declaration.name.text)) { + exports.set(declaration.name.text, declaration.name.text); + } + }); + } + } + }); + /** @param {import('typescript').Node} node */ function replace_jsdoc_type_tags(node) { if (node.jsDoc) { @@ -147,7 +194,10 @@ function tweak_types(content, names) { } }); - return code.toString(); + return { + code: code.toString(), + exports: Array.from(exports.keys()) + }; } catch { return null; } diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index c566d33605a7..036d780c8ca2 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -8,6 +8,7 @@ import { AdapterEntry, BodyValidator, CspDirectives, + JSONObject, JSONValue, Logger, MaybePromise, @@ -275,5 +276,25 @@ export interface SSRManifest { }; } +export interface GET = Record> { + (event: RequestEvent): MaybePromise +} + +export interface POST = Record> { + (event: RequestEvent): MaybePromise<{ status?: number, errors: Record, location?: never } | { status?: never, errors?: never, location: string } | void> +} + +export interface PUT = Record> { + (event: RequestEvent): MaybePromise<{ status?: number, errors: Record } | void> +} + +export interface PATCH = Record> { + (event: RequestEvent): MaybePromise<{ status?: number, errors: Record } | void> +} + +export interface DELETE = Record> { + (event: RequestEvent): MaybePromise +} + export function error(status: number, message?: string): HttpError; export function redirect(status: number, location?: string): Redirect; From a1b6026a004a5b67441aca8057bab25d78214682 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Tue, 2 Aug 2022 22:39:41 -0400 Subject: [PATCH 047/110] expose ServerData as Data when no load is specified --- packages/kit/src/core/sync/write_types.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index 16ea0a1e2a92..297ce8b681af 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -74,6 +74,10 @@ export function write_types(config, manifest_data) { declarations.push(`type ServerData = null;`); } + if (route.page.server && !route.page.module) { + exports.push(`export type Data = ServerData;`) + } + exports.push( `export type Load = GenericLoad;`, ...methods.map(name => `export type ${name} = Generic${name};`) From 938f07cf887c90f4b1a2758d8f535e119510f46f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 3 Aug 2022 15:34:03 -0400 Subject: [PATCH 048/110] replace magic number --- packages/kit/src/core/sync/write_types.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index 297ce8b681af..3f8383c4c531 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -122,8 +122,7 @@ function tweak_types(content, names) { }); } - // 93 because there's no ts.isExportKeyword function for some reason - if (node.modifiers?.some(modifier => modifier.kind === 93)) { + if (node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) { if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) { exports.set(node.name.text, node.name.text); } From d62d594214ba7ffcf00a0360b7226a32abf07066 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 3 Aug 2022 15:49:11 -0400 Subject: [PATCH 049/110] handle missing GET separately from failure --- packages/kit/src/core/sync/write_types.js | 42 +++++++++++++++-------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index 3f8383c4c531..35e6ecdb20f5 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -38,7 +38,11 @@ export function write_types(config, manifest_data) { ); if (route.type === 'page') { - imports.push(`import type {\n\t${['Load', ...methods].map(name => `${name} as Generic${name}`).join(',\n\t')}\n} from '@sveltejs/kit';`); + imports.push( + `import type {\n\t${['Load', ...methods] + .map((name) => `${name} as Generic${name}`) + .join(',\n\t')}\n} from '@sveltejs/kit';` + ); for (const node of route.layouts) { // TODO handle edge case where a layout doesn't have a sibling +page @@ -48,13 +52,17 @@ export function write_types(config, manifest_data) { const content = fs.readFileSync(route.page.module, 'utf8'); const proxy = tweak_types(content, module_names); - if (proxy && proxy.exports.includes('load')) { - const basename = path.basename(route.page.module); - write(`${outdir}/proxy${basename}`, proxy.code); - imports.push(`import { load } from './proxy${basename}';`); - exports.push(`export type Data = Awaited>;`); + if (proxy) { + if (proxy.exports.includes('load')) { + const basename = path.basename(route.page.module); + write(`${outdir}/proxy${basename}`, proxy.code); + imports.push(`import { load } from './proxy${basename}';`); + exports.push(`export type Data = Awaited>;`); + } else { + exports.push(`export type Data = ServerData;`); + } } else { - exports.push(`export type Data = any;`); + exports.push(`export type Data = unknown;`); } } @@ -62,7 +70,8 @@ export function write_types(config, manifest_data) { const content = fs.readFileSync(route.page.server, 'utf8'); const proxy = tweak_types(content, server_names); - if (proxy && proxy.exports.includes('GET')) { // TODO handle validation errors from POST/PUT/PATCH + if (proxy && proxy.exports.includes('GET')) { + // TODO handle validation errors from POST/PUT/PATCH const basename = path.basename(route.page.server); write(`${outdir}/proxy${basename}`, proxy.code); imports.push(`import { GET } from './proxy${basename}';`); @@ -75,12 +84,12 @@ export function write_types(config, manifest_data) { } if (route.page.server && !route.page.module) { - exports.push(`export type Data = ServerData;`) + exports.push(`export type Data = ServerData;`); } exports.push( `export type Load = GenericLoad;`, - ...methods.map(name => `export type ${name} = Generic${name};`) + ...methods.map((name) => `export type ${name} = Generic${name};`) ); } else { imports.push(`import type { RequestHandler as GenericRequestHandler } from '@sveltejs/kit';`); @@ -111,9 +120,9 @@ function tweak_types(content, names) { const exports = new Map(); - ast.forEachChild(node => { + ast.forEachChild((node) => { if (ts.isExportDeclaration(node) && ts.isNamedExports(node.exportClause)) { - node.exportClause.elements.forEach(element => { + node.exportClause.elements.forEach((element) => { const exported = element.name; if (names.has(element.name.text)) { const local = element.propertyName || element.name; @@ -122,13 +131,13 @@ function tweak_types(content, names) { }); } - if (node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword)) { + if (node.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) { if (ts.isFunctionDeclaration(node) && node.name?.text && names.has(node.name?.text)) { exports.set(node.name.text, node.name.text); } if (ts.isVariableStatement(node)) { - node.declarationList.declarations.forEach(declaration => { + node.declarationList.declarations.forEach((declaration) => { if (ts.isIdentifier(declaration.name) && names.has(declaration.name.text)) { exports.set(declaration.name.text, declaration.name.text); } @@ -188,7 +197,10 @@ function tweak_types(content, names) { if (add_parens) code.prependRight(arg.pos, '('); if (arg && !arg.type) { - code.appendLeft(arg.name.end, `: Parameters<${type}>[0]` + (add_parens ? ')' : '')); + code.appendLeft( + arg.name.end, + `: Parameters<${type}>[0]` + (add_parens ? ')' : '') + ); } } } From a40c1a5c5213ae27f009dbcb9960a40324c9a1cf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Wed, 3 Aug 2022 15:51:56 -0400 Subject: [PATCH 050/110] oops i think i did a bad git --- packages/kit/src/core/sync/write_types.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index 35e6ecdb20f5..a02de4becfe6 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -70,14 +70,18 @@ export function write_types(config, manifest_data) { const content = fs.readFileSync(route.page.server, 'utf8'); const proxy = tweak_types(content, server_names); - if (proxy && proxy.exports.includes('GET')) { - // TODO handle validation errors from POST/PUT/PATCH - const basename = path.basename(route.page.server); - write(`${outdir}/proxy${basename}`, proxy.code); - imports.push(`import { GET } from './proxy${basename}';`); - declarations.push(`type ServerData = Awaited>;`); + if (proxy) { + if (proxy.exports.includes('GET')) { + // TODO handle validation errors from POST/PUT/PATCH + const basename = path.basename(route.page.server); + write(`${outdir}/proxy${basename}`, proxy.code); + imports.push(`import { GET } from './proxy${basename}';`); + declarations.push(`type ServerData = Awaited>;`); + } else { + declarations.push(`type ServerData = null;`); + } } else { - declarations.push(`type ServerData = any;`); + declarations.push(`type ServerData = unknown;`); } } else { declarations.push(`type ServerData = null;`); From 939c82485a24900cd9b1305dbaa6a79f0ad4b6b2 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:12:57 -0400 Subject: [PATCH 051/110] remove typings tests, these are no longer useful --- packages/kit/package.json | 3 +- packages/kit/test/typings/endpoint.test.ts | 195 --------------------- packages/kit/test/typings/tsconfig.json | 7 - 3 files changed, 1 insertion(+), 204 deletions(-) delete mode 100644 packages/kit/test/typings/endpoint.test.ts delete mode 100644 packages/kit/test/typings/tsconfig.json diff --git a/packages/kit/package.json b/packages/kit/package.json index 2a4e5fa78bfe..a96e8d4849e5 100644 --- a/packages/kit/package.json +++ b/packages/kit/package.json @@ -68,10 +68,9 @@ "check:all": "tsc && pnpm -r --filter=\"./**\" check", "format": "npm run lint -- --write", "prepublishOnly": "npm run build", - "test": "npm run test:unit && npm run test:typings && npm run test:packaging && npm run test:integration", + "test": "npm run test:unit && npm run test:packaging && npm run test:integration", "test:integration": "pnpm run -r --workspace-concurrency 1 --filter=\"./test/**\" test", "test:unit": "uvu src \"(spec\\.js|test[\\\\/]index\\.js)\" -i packaging", - "test:typings": "tsc --project test/typings", "test:packaging": "uvu src/packaging \"(spec\\.js|test[\\\\/]index\\.js)\"", "types": "node scripts/extract-types.js", "postinstall": "node svelte-kit.js sync" diff --git a/packages/kit/test/typings/endpoint.test.ts b/packages/kit/test/typings/endpoint.test.ts deleted file mode 100644 index 558bd6d80719..000000000000 --- a/packages/kit/test/typings/endpoint.test.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { RequestHandler } from '@sveltejs/kit'; - -const valid_body = { - str: 'string', - num: 12345, - bool: true, - null: null, - maybe: Math.random() < 0.5 ? undefined : true, - custom: { - toJSON: () => 'custom toJSON function' - }, - list: ['string', 12345, false, null, undefined], - nested: { - another: 'string', - big_num: 98765, - binary: false, - nullish: null, - custom: { - toJSON: () => 'hi mom' - }, - list: [], - deeply: { - nested: {} - } - } -}; - -// valid - basic case of returning an object -export const base_case: RequestHandler = () => { - return { - body: valid_body - }; -}; - -// valid - raw Response instance should be allowed -export const response_instance: RequestHandler = () => { - return new Response(); -}; - -// valid - body instance of Uint8Array should be allowed -export const uint8array_body: RequestHandler = () => { - return { - body: new Uint8Array() - }; -}; - -// valid - body instance of ReadableStream should be allowed -export const readable_stream_body: RequestHandler = () => { - return { - body: new ReadableStream() - }; -}; - -// valid - different header pairs should be allowed -export const differential_headers_assignment: RequestHandler = () => { - if (Math.random() < 0.5) { - return { headers: { foo: 'bar' } }; - } else { - return { headers: { baz: 'foo' } }; - } -}; - -/** - * NOTE about type casting in body returned - * - * tests below with `{} as Interface` casts are there only for - * convenience purposes so we won't have to actually fill in the - * required data, it serves the exact same purpose and doesn't - * make the tests invalid - */ - -/** example json-serializable POJO */ -interface ExamplePost { - title: string; - description: string; - published_date?: string; - author_name?: string; - author_link?: string; -} -// valid - should not be any different -export const generic_case: RequestHandler, ExamplePost> = () => { - return { - body: {} as ExamplePost - }; -}; - -interface NestedChild { - message: string; -} -interface ParentWrapper { - fields: NestedChild; -} -export const nested_interfaces: RequestHandler, ParentWrapper> = () => { - return { - body: {} as ParentWrapper - }; -}; - -interface NestedLiteral { - date: { - published: string; - updated?: string; - time: { - hour: number; - min: number; - sec?: number; - }; - }; -} -export const nested_literal: RequestHandler, NestedLiteral> = () => { - return { - body: {} as NestedLiteral - }; -}; - -// --- invalid cases --- - -// @ts-expect-error - bigint cannot be converted to JSON string -export const error_unserializable_literal_bigint: RequestHandler = () => { - return { - body: BigInt('') - }; -}; -// @ts-expect-error - bigint cannot be converted to JSON string -export const error_unserializable_property_bigint: RequestHandler = () => { - return { - body: { - answer: BigInt('') - } - }; -}; -// @ts-expect-error - function cannot be converted to JSON string -export const error_unserializable_literal_function: RequestHandler = () => { - return { - body: () => {} - }; -}; -// @ts-expect-error - function cannot be converted to JSON string -export const error_unserializable_property_function: RequestHandler = () => { - return { - body: { - sorter() {} - } - }; -}; -// @ts-expect-error - symbol cannot be converted to JSON string -export const error_unserializable_literal_symbol: RequestHandler = () => { - return { - body: Symbol() - }; -}; -// @ts-expect-error - symbol cannot be converted to JSON string -export const error_unserializable_property_symbol: RequestHandler = () => { - return { - body: { - id: Symbol() - } - }; -}; - -/** example object that isn't serializable */ -interface InvalidPost { - sorter(a: any, b: any): number; -} -// @ts-expect-error - body must be JSON serializable with Generic passed -export const error_unserializable_generic: RequestHandler< - Record, - InvalidPost -> = () => { - return { - body: {} as InvalidPost - }; -}; - -// @ts-expect-error - body typed array must only be Uint8Array -export const error_other_typed_array_instances: RequestHandler = () => { - return { - body: new Uint16Array() - }; -}; - -// @ts-expect-error - instances cannot be nested -export const error_nested_instances: RequestHandler = () => { - return { - body: { typed: new Uint8Array() } - }; -}; - -// @ts-expect-error - instances cannot be nested -export const stream_readable_body: RequestHandler = async () => { - const { Readable } = await import('stream'); - return { - body: new Readable() - }; -}; diff --git a/packages/kit/test/typings/tsconfig.json b/packages/kit/test/typings/tsconfig.json deleted file mode 100644 index 9beabd92b571..000000000000 --- a/packages/kit/test/typings/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "noEmit": true - }, - "include": ["**/*.test.ts"] -} From de4580af75e9d744cf36c4ead604d047630f0332 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:15:01 -0400 Subject: [PATCH 052/110] tidy --- packages/kit/src/core/sync/write_types.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/kit/src/core/sync/write_types.js b/packages/kit/src/core/sync/write_types.js index a02de4becfe6..733e8325f877 100644 --- a/packages/kit/src/core/sync/write_types.js +++ b/packages/kit/src/core/sync/write_types.js @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import ts from 'typescript'; +import ts from 'typescript'; // TODO only do this transformation if there's a jsconfig/tsconfig and typescript is installed in the app? import MagicString from 'magic-string'; import { rimraf } from '../../utils/filesystem.js'; import { parse_route_id } from '../../utils/routing.js'; @@ -11,8 +11,6 @@ const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']; const module_names = new Set(['load']); const server_names = new Set(methods); -const page_exports = ['Load', 'GET', 'POST', 'PUT', 'PATCH', 'DELETE']; - /** * @param {import('types').ValidatedConfig} config * @param {import('types').ManifestData} manifest_data From 5e241d6999e2a1d59a8e8561875ca1759ab55bec Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:28:58 -0400 Subject: [PATCH 053/110] use server data if no load is provided --- packages/kit/src/runtime/server/page/load_node.js | 7 ++----- .../test/apps/basics/src/routes/xss/shadow/+page.server.js | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index d9c3c4e0a713..cea5369e68dc 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -28,9 +28,6 @@ export async function load_node({ event, options, state, route, node, $session } /** @type {Record} */ let server_data; - /** @type {Record} */ - let data; - if (node.server) { const should_prerender = node.module?.prerender ?? options.prerender.default; const mod = node.server; @@ -45,6 +42,8 @@ export async function load_node({ event, options, state, route, node, $session } server_data = {}; } + let data = server_data; + if (node.module?.load) { /** @type {import('types').LoadEvent} */ const load_event = { @@ -319,8 +318,6 @@ export async function load_node({ event, options, state, route, node, $session } // TODO unwrap top-level promises data = await node.module.load.call(null, load_event); - } else { - data = {}; } return { diff --git a/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js b/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js index 84ca34d49f6b..3a4a23fca6ca 100644 --- a/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/xss/shadow/+page.server.js @@ -1,4 +1,4 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ +/** @type {import('./$types').GET} */ export function GET() { const user = { name: '' From 86f34db1d4fd84d1d9d28f6d393208a671bc492f Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:31:35 -0400 Subject: [PATCH 054/110] temporarily disable non-SSR tests --- packages/kit/test/utils.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/kit/test/utils.js b/packages/kit/test/utils.js index 017f47585ae3..3b7faf1996f0 100644 --- a/packages/kit/test/utils.js +++ b/packages/kit/test/utils.js @@ -152,12 +152,12 @@ export const config = { }, retries: process.env.CI ? 5 : 0, projects: [ - { - name: `${test_browser}-${process.env.DEV ? 'dev' : 'build'}`, - use: { - javaScriptEnabled: true - } - }, + // { + // name: `${test_browser}-${process.env.DEV ? 'dev' : 'build'}`, + // use: { + // javaScriptEnabled: true + // } + // }, { name: `${test_browser}-${process.env.DEV ? 'dev' : 'build'}-no-js`, use: { From 35a76fcd474795220f839abfd410b34c269f1c14 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:37:30 -0400 Subject: [PATCH 055/110] apply headers --- packages/kit/src/runtime/server/index.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index bd05f186ca03..f7e0dd166bd1 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -142,7 +142,7 @@ export async function respond(request, options, state) { } // TODO apply these headers to the response - headers[key] = new_headers[key]; + headers[key.toLowerCase()] = new_headers[key]; } }, url @@ -257,6 +257,17 @@ export async function respond(request, options, state) { } if (response) { + for (const key in headers) { + const value = headers[key]; + if (key === 'set-cookie') { + for (const cookie in Array.isArray(value) ? value : [value]) { + response.headers.append(key, cookie); + } + } else { + response.headers.set(key, /** @type {string} */ (value)); + } + } + // respond with 304 if etag matches if (response.status === 200 && response.headers.has('etag')) { let if_none_match_value = request.headers.get('if-none-match'); From 08ca223e4dbd896e51abbf0aee8d5d9a1d3c2255 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:37:44 -0400 Subject: [PATCH 056/110] update test --- packages/kit/test/apps/basics/src/routes/caching/+page.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/kit/test/apps/basics/src/routes/caching/+page.js b/packages/kit/test/apps/basics/src/routes/caching/+page.js index 6890cce7d211..f826118b6117 100644 --- a/packages/kit/test/apps/basics/src/routes/caching/+page.js +++ b/packages/kit/test/apps/basics/src/routes/caching/+page.js @@ -1,4 +1,6 @@ -/** @type {import('@sveltejs/kit').Load} */ +/** @type {import('./$types').Load} */ export async function load({ setHeaders }) { - setHeaders('cache-control: max-age=30'); + setHeaders({ + 'cache-control': 'public, max-age=30' + }); } From 87d27194d2efa5bc6a8c3b97fb2a3d7dd932dd0a Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:38:02 -0400 Subject: [PATCH 057/110] allow load to return void --- packages/kit/types/index.d.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 036d780c8ca2..9d7323d7a960 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -182,7 +182,7 @@ export interface Load< InputData extends Record = Record, OutputData extends Record = Record > { - (event: LoadEvent): MaybePromise; + (event: LoadEvent): MaybePromise; } export interface LoadEvent< @@ -277,23 +277,33 @@ export interface SSRManifest { } export interface GET = Record> { - (event: RequestEvent): MaybePromise + (event: RequestEvent): MaybePromise; } export interface POST = Record> { - (event: RequestEvent): MaybePromise<{ status?: number, errors: Record, location?: never } | { status?: never, errors?: never, location: string } | void> + (event: RequestEvent): MaybePromise< + | { status?: number; errors: Record; location?: never } + | { status?: never; errors?: never; location: string } + | void + >; } export interface PUT = Record> { - (event: RequestEvent): MaybePromise<{ status?: number, errors: Record } | void> + (event: RequestEvent): MaybePromise<{ + status?: number; + errors: Record; + } | void>; } export interface PATCH = Record> { - (event: RequestEvent): MaybePromise<{ status?: number, errors: Record } | void> + (event: RequestEvent): MaybePromise<{ + status?: number; + errors: Record; + } | void>; } export interface DELETE = Record> { - (event: RequestEvent): MaybePromise + (event: RequestEvent): MaybePromise; } export function error(status: number, message?: string): HttpError; From 268f4707c9acd6a1dd0dab23301b631e719ced08 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:40:48 -0400 Subject: [PATCH 058/110] remove some tests that no longer make sense --- .../caching/private/has-session/+page.js | 10 --- .../caching/private/has-session/+page.svelte | 1 - .../private/uses-cache-private/+page.js | 8 -- .../private/uses-cache-private/+page.svelte | 1 - .../private/uses-fetch.json/+server.js | 8 -- .../caching/private/uses-fetch/+page.js | 15 ---- .../caching/private/uses-fetch/+page.svelte | 1 - .../private/uses-session-in-init/+page.js | 10 --- .../private/uses-session-in-init/+page.svelte | 7 -- .../private/uses-session-in-load/+page.js | 15 ---- .../private/uses-session-in-load/+page.svelte | 5 -- .../kit/test/apps/basics/test/server.test.js | 82 ------------------- 12 files changed, 163 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.js b/packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.js deleted file mode 100644 index 8075bbfe6925..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ url, setHeaders }) { - setHeaders( - `cache-control: ${ - url.searchParams.has('private') && url.searchParams.get('private') === 'true' - ? 'private' - : 'public' - }, max-age=30` - ); -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.svelte deleted file mode 100644 index 61a10746a647..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/has-session/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

this page will be private even if $session is not used, but a session is return from hooks.js#getSession

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.js b/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.js deleted file mode 100644 index a21953aa985b..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ url, setHeaders }) { - setHeaders( - `cache-control: ${ - url.searchParams.get('private') === 'true' ? 'private' : 'public' - }, max-age=30` - ); -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.svelte deleted file mode 100644 index dab86c8207ab..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-cache-private/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

this page will be cached for 30 seconds

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json/+server.js b/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json/+server.js deleted file mode 100644 index 67ed4c3a73cf..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch.json/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -export function GET() { - return new Response( - JSON.stringify({ - answer: 42 - }), - { headers: { 'content-type': 'application/json; charset=utf-8' } } - ); -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.js b/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.js deleted file mode 100644 index 36321aa7b161..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ url, setHeaders }) { - setHeaders( - `cache-control: ${ - url.searchParams.has('private') && url.searchParams.get('private') === 'true' - ? 'private' - : 'public' - }, max-age=30` - ); - const res = await fetch('/caching/private/uses-fetch.json', { - credentials: /** @type {RequestCredentials} */ (url.searchParams.get('credentials')) - }); - - return await res.json(); -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.svelte deleted file mode 100644 index dab86c8207ab..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-fetch/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

this page will be cached for 30 seconds

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.js b/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.js deleted file mode 100644 index 8075bbfe6925..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.js +++ /dev/null @@ -1,10 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ url, setHeaders }) { - setHeaders( - `cache-control: ${ - url.searchParams.has('private') && url.searchParams.get('private') === 'true' - ? 'private' - : 'public' - }, max-age=30` - ); -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.svelte deleted file mode 100644 index 5087b27622ec..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-init/+page.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

this page will be cached for 30 seconds ({session_exists})

diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.js b/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.js deleted file mode 100644 index 746181ed5494..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.js +++ /dev/null @@ -1,15 +0,0 @@ -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ url, session, setHeaders }) { - setHeaders( - `cache-control: ${ - url.searchParams.has('private') && url.searchParams.get('private') === 'true' - ? 'private' - : 'public' - }, max-age=30` - ); - const session_exists = !!session; - - return { - session_exists - }; -} diff --git a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.svelte b/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.svelte deleted file mode 100644 index f3e5ce7b46d5..000000000000 --- a/packages/kit/test/apps/basics/src/routes/caching/private/uses-session-in-load/+page.svelte +++ /dev/null @@ -1,5 +0,0 @@ - - -

this page will be cached for 30 seconds ({data.session_exists})

diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index 077724b3d1e1..f1ce6d7c9a21 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -13,88 +13,6 @@ test.describe('Caching', () => { const response = await request.get('/caching'); expect(response.headers()['cache-control']).toBe('public, max-age=30'); }); - - test('sets cache-control: private if page uses session in load and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-load'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: private if page uses session in init and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-init'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: private even if page doesnt use session but one exists and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/has-session'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: private if page uses fetch and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-fetch?credentials=include'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: public if page uses fetch without credentials and cache.private is unset', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-fetch?credentials=omit'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: private if cache.private is true', async ({ request }) => { - const response = await request.get('/caching/private/uses-cache-private?private=true'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); - - test('sets cache-control: public if cache.private is false', async ({ request }) => { - const response = await request.get('/caching/private/uses-cache-private?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page uses session in load and cache.private is false', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-load?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page uses session in init and cache.private is false', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-session-in-init?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page has session and cache.private is false', async ({ - request - }) => { - const response = await request.get('/caching/private/has-session?private=false'); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: public if page uses fetch and cache.private is false', async ({ - request - }) => { - const response = await request.get( - '/caching/private/uses-fetch?credentials=include&private=false' - ); - expect(response.headers()['cache-control']).toBe('public, max-age=30'); - }); - - test('sets cache-control: private if page uses fetch without credentials and cache.private is true', async ({ - request - }) => { - const response = await request.get('/caching/private/uses-fetch?credentials=omit&private=true'); - expect(response.headers()['cache-control']).toBe('private, max-age=30'); - }); }); test.describe('Content-Type', () => { From c171deb193b5a265026a501b1a5afb7fdd9aeeab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 15:53:34 -0400 Subject: [PATCH 059/110] remove some more unused tests --- .../basics/src/routes/etag/binary/+server.js | 8 --- .../basics/src/routes/etag/custom/+server.js | 11 ---- .../basics/src/routes/etag/text/+server.js | 8 --- .../kit/test/apps/basics/test/server.test.js | 59 ------------------- 4 files changed, 86 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/etag/binary/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/etag/custom/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/etag/text/+server.js diff --git a/packages/kit/test/apps/basics/src/routes/etag/binary/+server.js b/packages/kit/test/apps/basics/src/routes/etag/binary/+server.js deleted file mode 100644 index 923f32aab310..000000000000 --- a/packages/kit/test/apps/basics/src/routes/etag/binary/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response(new Uint8Array([1, 2, 3, 4, 5]), { - headers: { - 'content-type': 'application/octet-stream' - } - }); -} diff --git a/packages/kit/test/apps/basics/src/routes/etag/custom/+server.js b/packages/kit/test/apps/basics/src/routes/etag/custom/+server.js deleted file mode 100644 index fd76685d0af0..000000000000 --- a/packages/kit/test/apps/basics/src/routes/etag/custom/+server.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET({ request }) { - if (request.headers.get('if-none-match') === '@1234@') - return new Response(undefined, { status: 304 }); - - return new Response(`${Math.random()}`, { - headers: { - etag: '@1234@' - } - }); -} diff --git a/packages/kit/test/apps/basics/src/routes/etag/text/+server.js b/packages/kit/test/apps/basics/src/routes/etag/text/+server.js deleted file mode 100644 index e95e55e0919d..000000000000 --- a/packages/kit/test/apps/basics/src/routes/etag/text/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response('some text', { - headers: { - expires: 'yesterday' - } - }); -} diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index f1ce6d7c9a21..539385009fc5 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -22,65 +22,6 @@ test.describe('Content-Type', () => { }); }); -test.describe('ETags', () => { - test('generates etag/304 for text body', async ({ request }) => { - const r1 = await request.get('/etag/text'); - const etag = r1.headers()['etag']; - expect(etag).toBeTruthy(); - - const r2 = await request.get('/etag/text', { - headers: { - 'if-none-match': etag - } - }); - - expect(r2.status()).toBe(304); - expect(r2.headers()['expires']).toBe('yesterday'); - }); - - test('generates etag/304 for binary body', async ({ request }) => { - const r1 = await request.get('/etag/binary'); - const etag = r1.headers()['etag']; - expect(etag).toBeTruthy(); - - const r2 = await request.get('/etag/binary', { - headers: { - 'if-none-match': etag - } - }); - - expect(r2.status()).toBe(304); - }); - - test('support W/ etag prefix', async ({ request }) => { - const r1 = await request.get('/etag/text'); - const etag = r1.headers()['etag']; - expect(etag).toBeTruthy(); - - const r2 = await request.get('/etag/text', { - headers: { - 'if-none-match': `W/${etag}` - } - }); - - expect(r2.status()).toBe(304); - }); - - test('custom etag', async ({ request }) => { - const r1 = await request.get('/etag/custom'); - const etag = r1.headers()['etag']; - expect(etag).toBe('@1234@'); - - const r2 = await request.get('/etag/custom', { - headers: { - 'if-none-match': '@1234@' - } - }); - - expect(r2.status()).toBe(304); - }); -}); - test.describe('Endpoints', () => { test('200 status on empty endpoint', async ({ request }) => { const response = await request.get('/endpoint-output/empty'); From d04e5aa45b9afecc3ef5614fe2f7784e73718489 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 16:06:04 -0400 Subject: [PATCH 060/110] this is fun. delete delete delete --- .../routes/endpoint-output/empty/+server.js | 4 - .../endpoint-output/headers-object/+server.js | 8 -- .../routes/endpoint-output/headers/+server.js | 8 -- .../routes/endpoint-output/null/+server.js | 4 - .../routes/endpoint-output/proxy/+server.js | 2 - .../routes/endpoint-output/simple/+server.js | 8 -- .../endpoint-output/xml-bytes/+server.js | 8 -- .../endpoint-output/xml-text/+server.js | 5 -- .../kit/test/apps/basics/test/server.test.js | 88 ++----------------- 9 files changed, 7 insertions(+), 128 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/empty/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/headers/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/null/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/proxy/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/simple/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text/+server.js diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/empty/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/empty/+server.js deleted file mode 100644 index 1903715676a7..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/empty/+server.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response(undefined); -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object/+server.js deleted file mode 100644 index 2ec1fdd9e837..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers-object/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response(undefined, { - headers: new Headers({ - 'X-Foo': 'bar' - }) - }); -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/headers/+server.js deleted file mode 100644 index 7a2094895349..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/headers/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response(undefined, { - headers: { - 'Set-Cookie': 'foo=bar' - } - }); -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/null/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/null/+server.js deleted file mode 100644 index 0aeea43270aa..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/null/+server.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response(null); -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/proxy/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/proxy/+server.js deleted file mode 100644 index 02fd26bed3a6..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/proxy/+server.js +++ /dev/null @@ -1,2 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export const GET = ({ url }) => fetch(`http://localhost:${url.searchParams.get('port')}`); diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/simple/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/simple/+server.js deleted file mode 100644 index 67ed4c3a73cf..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/simple/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -export function GET() { - return new Response( - JSON.stringify({ - answer: 42 - }), - { headers: { 'content-type': 'application/json; charset=utf-8' } } - ); -} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes/+server.js deleted file mode 100644 index 8f2a80aa46ce..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-bytes/+server.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export const GET = () => { - const body = ''; - return new Response(new TextEncoder().encode(body), { - headers: { 'content-type': 'application/xml' }, - status: 200 - }); -}; diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text/+server.js deleted file mode 100644 index 5123674d66cf..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/xml-text/+server.js +++ /dev/null @@ -1,5 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export const GET = () => { - const body = ''; - return new Response(body, { headers: { 'content-type': 'application/xml' }, status: 200 }); -}; diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index 539385009fc5..6e26e86ba4a5 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -1,5 +1,5 @@ import { expect } from '@playwright/test'; -import { start_server, test } from '../../../utils.js'; +import { test } from '../../../utils.js'; import { createHash, randomBytes } from 'node:crypto'; /** @typedef {import('@playwright/test').Response} Response */ @@ -23,18 +23,6 @@ test.describe('Content-Type', () => { }); test.describe('Endpoints', () => { - test('200 status on empty endpoint', async ({ request }) => { - const response = await request.get('/endpoint-output/empty'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(await response.json()).toEqual({}); - }); - - test('set-cookie without body', async ({ request }) => { - const response = await request.get('/endpoint-output/headers'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(response.headers()['set-cookie']).toBeDefined(); - }); - test('HEAD with matching headers but without body', async ({ request }) => { const url = '/endpoint-output/body'; @@ -61,63 +49,9 @@ test.describe('Endpoints', () => { expect(headers.head).toEqual(headers.get); }); - test('200 status by default', async ({ request }) => { - const response = await request.get('/endpoint-output/body'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(await response.text()).toBe('{}'); - }); - - // TODO are these tests useful? - test('always returns a body', async ({ request }) => { - const response = await request.get('/endpoint-output/empty'); - expect(typeof (await response.body())).toEqual('object'); - }); - - test('null body returns null json value', async ({ request }) => { - const response = await request.get('/endpoint-output/null'); - expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(200); - expect(await response.json()).toBe(null); - }); - - test('gets string response with XML Content-Type', async ({ request }) => { - const response = await request.get('/endpoint-output/xml-text'); - - expect(response.headers()['content-type']).toBe('application/xml'); - expect(await response.text()).toBe(''); - }); - - test('gets binary response with XML Content-Type', async ({ request }) => { - const response = await request.get('/endpoint-output/xml-bytes'); - - expect(response.headers()['content-type']).toBe('application/xml'); - expect(await response.text()).toBe(''); - }); - - test('allows headers to be a Headers object', async ({ request }) => { - const response = await request.get('/endpoint-output/headers-object'); - - expect(response.headers()['x-foo']).toBe('bar'); - }); - - test('allows return value to be a Response', async ({ request }) => { - const { port, close } = await start_server((req, res) => { - res.writeHead(200, { - 'X-Foo': 'bar' - }); - - res.end('ok'); - }); - - try { - const response = await request.get(`/endpoint-output/proxy?port=${port}`); - - expect(await response.text()).toBe('ok'); - expect(response.headers()['x-foo']).toBe('bar'); - } finally { - await close(); - } - }); - + // TODO all the remaining tests in this section are really only testing + // setResponse, since we're not otherwise changing anything on the response. + // might be worth making these unit tests instead test('multiple set-cookie on endpoints using GET', async ({ request }) => { const response = await request.get('/set-cookie'); @@ -133,17 +67,7 @@ test.describe('Endpoints', () => { ]); }); - test('Standalone endpoint is not accessible via /__data.json suffix', async ({ request }) => { - const r1 = await request.get('/endpoint-output/simple', { - headers: { accept: 'application/json' } - }); - - expect(await r1.json()).toEqual({ answer: 42 }); - - const r2 = await request.get('/endpoint-output/simple/__data.json'); - expect(r2.status()).toBe(404); - }); - + // TODO see above test('body can be a binary ReadableStream', async ({ request }) => { const interruptedResponse = request.get('/endpoint-output/stream-throw-error'); await expect(interruptedResponse).rejects.toThrow('socket hang up'); @@ -154,6 +78,7 @@ test.describe('Endpoints', () => { expect(response.headers()['digest']).toEqual(`sha-256=${digest}`); }); + // TODO see above test('stream can be canceled with TypeError', async ({ request }) => { const responseBefore = await request.get('/endpoint-output/stream-typeerror?what'); expect(await responseBefore.text()).toEqual('null'); @@ -165,6 +90,7 @@ test.describe('Endpoints', () => { expect(await responseAfter.text()).toEqual('TypeError'); }); + // TODO see above test('request body can be read slow', async ({ request }) => { const data = randomBytes(1024 * 256); const digest = createHash('sha256').update(data).digest('base64url'); From fa955b790fc52822cbb71ecc2e9e0d93991a0a26 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 16:07:23 -0400 Subject: [PATCH 061/110] fix test --- .../src/routes/endpoint-output/stream-typeerror/+server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js index 8b6c7e5f32ad..bf9a11a6fb95 100644 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js +++ b/packages/kit/test/apps/basics/src/routes/endpoint-output/stream-typeerror/+server.js @@ -1,10 +1,10 @@ -let errorName = null; +let errorName = 'null'; /** @type {import('@sveltejs/kit').RequestHandler} */ export function GET({ url }) { if (url.searchParams.has('what')) { const body = errorName; - errorName = null; + errorName = 'null'; return new Response(body); } From ebe35e0a82d30756123775373429d10a13b8e087 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 17:05:44 -0400 Subject: [PATCH 062/110] handle accept:json requests --- packages/kit/src/runtime/server/endpoint.js | 27 ++------ packages/kit/src/runtime/server/page/index.js | 65 ++++++++++++++++++- packages/kit/src/runtime/server/utils.js | 25 +++++++ .../routes/endpoint-output/fetched/+server.js | 7 -- .../get-explicit/+page.server.js | 3 +- .../routes/shadowed/simple/+page.server.js | 2 +- .../kit/test/apps/basics/test/server.test.js | 6 +- 7 files changed, 101 insertions(+), 34 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/endpoint-output/fetched/+server.js diff --git a/packages/kit/src/runtime/server/endpoint.js b/packages/kit/src/runtime/server/endpoint.js index 415d9d9c3975..22eda3a20599 100644 --- a/packages/kit/src/runtime/server/endpoint.js +++ b/packages/kit/src/runtime/server/endpoint.js @@ -1,4 +1,4 @@ -import { check_method_names } from './utils.js'; +import { check_method_names, method_not_allowed } from './utils.js'; /** * @param {import('types').RequestEvent} event @@ -21,28 +21,13 @@ export async function render_endpoint(event, route) { } if (!handler) { - const allowed = []; - - for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) { - if (mod[method]) allowed.push(method); + if (event.request.headers.get('x-sveltekit-load')) { + // TODO would be nice to avoid these requests altogether, + // by noting whether or not page endpoints export `get` + return new Response(undefined, { status: 204 }); } - if (mod.GET || mod.HEAD) allowed.push('HEAD'); - - return event.request.headers.get('x-sveltekit-load') - ? // TODO would be nice to avoid these requests altogether, - // by noting whether or not page endpoints export `get` - new Response(undefined, { - status: 204 - }) - : new Response(`${method} method not allowed`, { - status: 405, - headers: { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 - // "The server must generate an Allow header field in a 405 status code response" - allow: allowed.join(', ') - } - }); + return method_not_allowed(mod, method); } const response = await handler(event); diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 5b5f06e54f59..c20217e6df2d 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -3,6 +3,8 @@ import { render_response } from './render.js'; import { load_node } from './load_node.js'; import { respond_with_error } from './respond_with_error.js'; import { coalesce_to_error } from '../../../utils/error.js'; +import { method_not_allowed } from '../utils.js'; +import { HttpError, Redirect } from '../../../index/private.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -33,7 +35,10 @@ export async function render_page(event, route, options, state, resolve_opts) { ]); if (accept === 'application/json') { - throw new Error('TODO return JSON'); + const node = await options.manifest._.nodes[route.page](); + if (node.server) { + return handle_json_request(event, node.server); + } } const $session = await options.hooks.getSession(event); @@ -211,3 +216,61 @@ function get_page_config(leaf, options) { hydrate: leaf.module?.hydrate ?? options.hydrate }; } + +/** + * @param {import('types').RequestEvent} event + * @param {import('types').SSRNode['server']} mod + */ +async function handle_json_request(event, mod) { + const method = /** @type {import('types').HttpMethod} */ (event.request.method); + const handler = mod[method === 'HEAD' ? 'GET' : method]; + + if (!handler) { + return method_not_allowed(mod, method); + } + + try { + const result = await handler.call(null, event); + + if (method === 'HEAD') { + return new Response(); + } + + if (method === 'GET') { + return json_response(result, 200); + } + + if (method === 'POST') { + if (result.errors) { + return json_response({ errors: result.errors }, result.status || 400); + } + + return new Response(undefined, { + status: 201, + headers: result.location ? { location: result.location } : undefined + }); + } + + return new Response(undefined, { status: 204 }); + } catch (error) { + if (error instanceof Redirect) { + return Response.redirect(error.location, error.status); + } + + return json_response({ message: error.message }, error.status || 500); + } +} + +/** + * @param {any} data + * @param {number} status + */ +function json_response(data, status) { + // TODO replace with Response.json one day + return new Response(JSON.stringify(data), { + status, + headers: { + 'content-type': 'application/json; charset=utf-8' + } + }); +} diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index 3d9c1ba1d1cc..7b7ff7e83248 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -72,3 +72,28 @@ export function check_method_names(mod) { export const GENERIC_ERROR = { id: '__error' }; + +/** + * + * @param {Record} mod + * @param {import('types').HttpMethod} method + * @returns + */ +export function method_not_allowed(mod, method) { + const allowed = []; + + for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) { + if (method in mod) allowed.push(method); + } + + if (mod.GET || mod.HEAD) allowed.push('HEAD'); + + return new Response(`${method} method not allowed`, { + status: 405, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: allowed.join(', ') + } + }); +} diff --git a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetched/+server.js b/packages/kit/test/apps/basics/src/routes/endpoint-output/fetched/+server.js deleted file mode 100644 index b7420f5d170d..000000000000 --- a/packages/kit/test/apps/basics/src/routes/endpoint-output/fetched/+server.js +++ /dev/null @@ -1,7 +0,0 @@ -export function GET() { - return new Response('ok', { - headers: { - 'x-foo': 'bar' - } - }); -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js index 954b12d84be8..169593448a7c 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/errors/page-endpoint/get-explicit/+page.server.js @@ -1,6 +1,5 @@ import { error } from '@sveltejs/kit'; -import { FancyError } from '../_shared.js'; export const GET = () => { - throw error(400, new FancyError('oops')); + throw error(400, 'oops'); }; diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js b/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js index c4cdd89104e4..6fc9085d48f9 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js +++ b/packages/kit/test/apps/basics/src/routes/shadowed/simple/+page.server.js @@ -1,4 +1,4 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ +/** @type {import('./$types').GET} */ export function GET({ locals }) { return { answer: locals.answer diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index 6e26e86ba4a5..d4e64f90070e 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -104,7 +104,9 @@ test.describe('Errors', () => { const response = await request.get('/errors/invalid-route-response'); expect(/** @type {import('@playwright/test').APIResponse} */ (response).status()).toBe(500); - expect(await response.text()).toMatch('expected an object'); + expect(await response.text()).toMatch( + 'Invalid response from route /errors/invalid-route-response: handler should return a Response object' + ); }); // TODO before we implemented route fallthroughs, and there was a 1:1 @@ -156,7 +158,7 @@ test.describe('Errors', () => { const { message, name, stack } = await response.json(); expect(response.status()).toBe(400); - expect(name).toBe('FancyError'); + expect(name).toBe('Error'); expect(message).toBe('oops'); if (process.env.DEV) { From 262f48700f91a644e090b0d200553c036501260b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 17:06:40 -0400 Subject: [PATCH 063/110] fix test --- packages/kit/test/apps/basics/test/server.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index d4e64f90070e..a9bb172341a1 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -250,7 +250,10 @@ test.describe('Shadowed pages', () => { delete headers.get[name]; }); - expect(headers.head).toEqual(headers.get); + expect(headers.get).toEqual({ + ...headers.head, + 'content-type': 'application/json; charset=utf-8' + }); }); test('Responds from endpoint if Accept includes application/json but not text/html', async ({ From 04da619054098836c133fec5c64c693bf043b538 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 18:01:12 -0400 Subject: [PATCH 064/110] small tidy up --- packages/kit/src/runtime/server/page/index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index c20217e6df2d..afefbf3720a5 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -4,7 +4,7 @@ import { load_node } from './load_node.js'; import { respond_with_error } from './respond_with_error.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { method_not_allowed } from '../utils.js'; -import { HttpError, Redirect } from '../../../index/private.js'; +import { Redirect } from '../../../index/private.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -154,9 +154,6 @@ export async function render_page(event, route, options, state, resolve_opts) { } } - // TODO backtrack until we find an __error.svelte component - // that we can use as the leaf node - // for now just return regular error page return await respond_with_error({ event, options, From 84af4df7224922faa042cb4044fa89e9c158444e Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 18:07:18 -0400 Subject: [PATCH 065/110] tidy --- packages/kit/src/runtime/server/page/index.js | 2 +- packages/kit/src/runtime/server/page/render.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index afefbf3720a5..7748f2e82749 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -141,7 +141,7 @@ export async function render_page(event, route, options, state, resolve_opts) { fetched: [] }); - page_config = get_page_config(error_node, options); + page_config = { router: true, hydrate: true }; branch = branch.slice(0, j + 1).concat(error_loaded); break ssr; } catch (err) { diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 756092bdf31a..3d416511599c 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -84,7 +84,6 @@ export async function render_response({ Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v)); } - // TODO probably better if `fetched` wasn't populated unless `hydrate` if (fetched && page_config.hydrate) serialized_data.push(...fetched); } From 8b6f0461a63ed42032d6cc945dcac7c0deb14481 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 18:31:46 -0400 Subject: [PATCH 066/110] simplify --- packages/kit/src/runtime/server/page/index.js | 134 ++++++++---------- 1 file changed, 61 insertions(+), 73 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 7748f2e82749..04af85aeebac 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -44,6 +44,7 @@ export async function render_page(event, route, options, state, resolve_opts) { const $session = await options.hooks.getSession(event); // TODO for non-GET requests, first call handler in +page.server.js + // (this also determines status code) if (!resolve_opts.ssr) { return await render_response({ @@ -85,86 +86,73 @@ export async function render_page(event, route, options, state, resolve_opts) { } } - /** @type {Array} */ + /** @type {Array} */ let branch = []; /** @type {number} */ let status = 200; - /** @type {Error | null} */ - let error = null; - - ssr: { - for (let i = 0; i < nodes.length; i += 1) { - const node = nodes[i]; - - /** @type {Loaded | undefined} */ - let loaded; - - if (node) { - try { - loaded = await load_node({ - event, - options, - state, - route, - $session, - node - }); - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); - - status = 500; - error = e; - } - - if (loaded && !error) { - branch.push(loaded); - } - - if (error) { - while (i--) { - if (route.errors[i]) { - const index = /** @type {number} */ (route.errors[i]); - const error_node = await options.manifest._.nodes[index](); - - let j = i; - while (!branch[j]) j -= 1; - - try { - const error_loaded = /** @type {import('./types').Loaded} */ ({ - node: error_node, - data: {}, - server_data: {}, - fetched: [] - }); - - page_config = { router: true, hydrate: true }; - branch = branch.slice(0, j + 1).concat(error_loaded); - break ssr; - } catch (err) { - const e = coalesce_to_error(err); - - options.handle_error(e, event); - - continue; - } - } + for (let i = 0; i < nodes.length; i += 1) { + const node = nodes[i]; + + /** @type {Loaded | undefined} */ + let loaded; + + if (node) { + try { + loaded = await load_node({ + event, + options, + state, + route, + $session, + node + }); + + branch.push(loaded); + } catch (err) { + const error = coalesce_to_error(err); + + options.handle_error(error, event); + + status = 500; + + while (i--) { + if (route.errors[i]) { + const index = /** @type {number} */ (route.errors[i]); + const error_node = await options.manifest._.nodes[index](); + + let j = i; + while (!branch[j]) j -= 1; + + const error_loaded = /** @type {import('./types').Loaded} */ ({ + node: error_node, + data: {}, + server_data: {}, + fetched: [] + }); + + return await render_response({ + event, + options, + state, + $session, + resolve_opts, + page_config: { router: true, hydrate: true }, + status, + error, + branch: branch + .slice(0, j + 1) + .filter(Boolean) + .concat(error_loaded) + }); } - - return await respond_with_error({ - event, - options, - state, - $session, - status, - error, - resolve_opts - }); } } + } else { + // push an empty slot so we can rewind past gaps to the + // layout that corresponds with an +error.svelte page + branch.push(null); } } @@ -176,7 +164,7 @@ export async function render_page(event, route, options, state, resolve_opts) { resolve_opts, page_config, status, - error, + error: null, branch: branch.filter(Boolean) }); } catch (err) { From 81d112c3b8b9c2c4407324772a2979c1c701211c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 19:15:47 -0400 Subject: [PATCH 067/110] simplify, improve efficiency --- packages/kit/src/runtime/server/page/fetch.js | 265 +++++++++++++++++ packages/kit/src/runtime/server/page/index.js | 22 +- .../kit/src/runtime/server/page/load_node.js | 267 +----------------- .../kit/src/runtime/server/page/render.js | 8 +- .../runtime/server/page/respond_with_error.js | 11 +- .../kit/src/runtime/server/page/types.d.ts | 11 +- 6 files changed, 302 insertions(+), 282 deletions(-) create mode 100644 packages/kit/src/runtime/server/page/fetch.js diff --git a/packages/kit/src/runtime/server/page/fetch.js b/packages/kit/src/runtime/server/page/fetch.js new file mode 100644 index 000000000000..de21cdc40389 --- /dev/null +++ b/packages/kit/src/runtime/server/page/fetch.js @@ -0,0 +1,265 @@ +import * as cookie from 'cookie'; +import * as set_cookie_parser from 'set-cookie-parser'; +import { respond } from '../index.js'; +import { is_root_relative, resolve } from '../../../utils/url.js'; +import { domain_matches, path_matches } from './cookie.js'; + +/** + * @param {{ + * event: import('types').RequestEvent; + * options: import('types').SSROptions; + * state: import('types').SSRState; + * route: import('types').SSRPage | import('types').SSRErrorPage; + * }} opts + */ +export function create_fetch({ event, options, state, route }) { + /** @type {import('./types').Fetched[]} */ + const fetched = []; + + const cookies = cookie.parse(event.request.headers.get('cookie') || ''); + + /** @type {import('set-cookie-parser').Cookie[]} */ + const new_cookies = []; + + /** @type {typeof fetch} */ + const fetcher = async (resource, opts = {}) => { + /** @type {string} */ + let requested; + + if (typeof resource === 'string' || resource instanceof URL) { + requested = resource.toString(); + } else { + requested = resource.url; + + opts = { + method: resource.method, + headers: resource.headers, + body: resource.body, + mode: resource.mode, + credentials: resource.credentials, + cache: resource.cache, + redirect: resource.redirect, + referrer: resource.referrer, + integrity: resource.integrity, + ...opts + }; + } + + opts.headers = new Headers(opts.headers); + + // merge headers from request + for (const [key, value] of event.request.headers) { + if ( + key !== 'authorization' && + key !== 'connection' && + key !== 'cookie' && + key !== 'host' && + key !== 'if-none-match' && + !opts.headers.has(key) + ) { + opts.headers.set(key, value); + } + } + + const resolved = resolve(event.url.pathname, requested.split('?')[0]); + + /** @type {Response} */ + let response; + + /** @type {import('types').PrerenderDependency} */ + let dependency; + + // handle fetch requests for static assets. e.g. prebaked data, etc. + // we need to support everything the browser's fetch supports + const prefix = options.paths.assets || options.paths.base; + const filename = decodeURIComponent( + resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved + ).slice(1); + const filename_html = `${filename}/index.html`; // path may also match path/index.html + + const is_asset = options.manifest.assets.has(filename); + const is_asset_html = options.manifest.assets.has(filename_html); + + if (is_asset || is_asset_html) { + const file = is_asset ? filename : filename_html; + + if (options.read) { + const type = is_asset + ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))] + : 'text/html'; + + response = new Response(options.read(file), { + headers: type ? { 'content-type': type } : {} + }); + } else { + response = await fetch(`${event.url.origin}/${file}`, /** @type {RequestInit} */ (opts)); + } + } else if (is_root_relative(resolved)) { + if (opts.credentials !== 'omit') { + const authorization = event.request.headers.get('authorization'); + + // combine cookies from the initiating request with any that were + // added via set-cookie + const combined_cookies = { ...cookies }; + + for (const cookie of new_cookies) { + if (!domain_matches(event.url.hostname, cookie.domain)) continue; + if (!path_matches(resolved, cookie.path)) continue; + + combined_cookies[cookie.name] = cookie.value; + } + + const cookie = Object.entries(combined_cookies) + .map(([name, value]) => `${name}=${value}`) + .join('; '); + + if (cookie) { + opts.headers.set('cookie', cookie); + } + + if (authorization && !opts.headers.has('authorization')) { + opts.headers.set('authorization', authorization); + } + } + + if (opts.body && typeof opts.body !== 'string') { + // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a + // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object. + // non-string bodies are irksome to deal with, but luckily aren't particularly useful + // in this context anyway, so we take the easy route and ban them + throw new Error('Request body must be a string'); + } + + response = await respond( + new Request(new URL(requested, event.url).href, { ...opts }), + options, + { + ...state, + initiator: route + } + ); + + if (state.prerendering) { + dependency = { response, body: null }; + state.prerendering.dependencies.set(resolved, dependency); + } + } else { + // external + if (resolved.startsWith('//')) { + requested = event.url.protocol + requested; + } + + // external fetch + // allow cookie passthrough for "same-origin" + // if SvelteKit is serving my.domain.com: + // - domain.com WILL NOT receive cookies + // - my.domain.com WILL receive cookies + // - api.domain.dom WILL NOT receive cookies + // - sub.my.domain.com WILL receive cookies + // ports do not affect the resolution + // leading dot prevents mydomain.com matching domain.com + if ( + `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) && + opts.credentials !== 'omit' + ) { + const cookie = event.request.headers.get('cookie'); + if (cookie) opts.headers.set('cookie', cookie); + } + + // we need to delete the connection header, as explained here: + // https://github.com/nodejs/undici/issues/1470#issuecomment-1140798467 + // TODO this may be a case for being selective about which headers we let through + opts.headers.delete('connection'); + + const external_request = new Request(requested, /** @type {RequestInit} */ (opts)); + response = await options.hooks.externalFetch.call(null, external_request); + } + + const set_cookie = response.headers.get('set-cookie'); + if (set_cookie) { + new_cookies.push( + ...set_cookie_parser + .splitCookiesString(set_cookie) + .map((str) => set_cookie_parser.parseString(str)) + ); + } + + const proxy = new Proxy(response, { + get(response, key, _receiver) { + async function text() { + const body = await response.text(); + + /** @type {import('types').ResponseHeaders} */ + const headers = {}; + for (const [key, value] of response.headers) { + // TODO skip others besides set-cookie and etag? + if (key !== 'set-cookie' && key !== 'etag') { + headers[key] = value; + } + } + + if (!opts.body || typeof opts.body === 'string') { + const status_number = Number(response.status); + if (isNaN(status_number)) { + throw new Error( + `response.status is not a number. value: "${ + response.status + }" type: ${typeof response.status}` + ); + } + + fetched.push({ + url: requested, + body: opts.body, + response: { + status: status_number, + statusText: response.statusText, + headers, + body + } + }); + } + + if (dependency) { + dependency.body = body; + } + + return body; + } + + if (key === 'arrayBuffer') { + return async () => { + const buffer = await response.arrayBuffer(); + + if (dependency) { + dependency.body = new Uint8Array(buffer); + } + + // TODO should buffer be inlined into the page (albeit base64'd)? + // any conditions in which it shouldn't be? + + return buffer; + }; + } + + if (key === 'text') { + return text; + } + + if (key === 'json') { + return async () => { + return JSON.parse(await text()); + }; + } + + // TODO arrayBuffer? + + return Reflect.get(response, key, response); + } + }); + + return proxy; + }; + + return { fetcher, fetched }; +} diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 04af85aeebac..cc1f767069ec 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -5,6 +5,7 @@ import { respond_with_error } from './respond_with_error.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { method_not_allowed } from '../utils.js'; import { Redirect } from '../../../index/private.js'; +import { create_fetch } from './fetch.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -43,12 +44,15 @@ export async function render_page(event, route, options, state, resolve_opts) { const $session = await options.hooks.getSession(event); + const { fetcher, fetched } = create_fetch({ event, options, state, route }); + // TODO for non-GET requests, first call handler in +page.server.js // (this also determines status code) if (!resolve_opts.ssr) { return await render_response({ branch: [], + fetched, page_config: { hydrate: true, router: true @@ -95,18 +99,15 @@ export async function render_page(event, route, options, state, resolve_opts) { for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; - /** @type {Loaded | undefined} */ - let loaded; - if (node) { try { - loaded = await load_node({ + const loaded = await load_node({ event, options, state, - route, $session, - node + node, + fetcher }); branch.push(loaded); @@ -128,8 +129,7 @@ export async function render_page(event, route, options, state, resolve_opts) { const error_loaded = /** @type {import('./types').Loaded} */ ({ node: error_node, data: {}, - server_data: {}, - fetched: [] + server_data: {} }); return await render_response({ @@ -144,7 +144,8 @@ export async function render_page(event, route, options, state, resolve_opts) { branch: branch .slice(0, j + 1) .filter(Boolean) - .concat(error_loaded) + .concat(error_loaded), + fetched }); } } @@ -165,7 +166,8 @@ export async function render_page(event, route, options, state, resolve_opts) { page_config, status, error: null, - branch: branch.filter(Boolean) + branch: branch.filter(Boolean), + fetched }); } catch (err) { const error = coalesce_to_error(err); diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_node.js index cea5369e68dc..50b838a2e6b5 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_node.js @@ -1,8 +1,4 @@ -import * as cookie from 'cookie'; -import * as set_cookie_parser from 'set-cookie-parser'; -import { respond } from '../index.js'; -import { LoadURL, PrerenderingURL, is_root_relative, resolve } from '../../../utils/url.js'; -import { domain_matches, path_matches } from './cookie.js'; +import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; /** * Calls the user's `load` function. @@ -10,21 +6,13 @@ import { domain_matches, path_matches } from './cookie.js'; * event: import('types').RequestEvent; * options: import('types').SSROptions; * state: import('types').SSRState; - * route: import('types').SSRPage | import('types').SSRErrorPage; * node: import('types').SSRNode; + * fetcher: typeof fetch; * $session: any; * }} opts * @returns {Promise} */ -export async function load_node({ event, options, state, route, node, $session }) { - /** @type {Array} */ - const fetched = []; - - const cookies = cookie.parse(event.request.headers.get('cookie') || ''); - - /** @type {import('set-cookie-parser').Cookie[]} */ - const new_cookies = []; - +export async function load_node({ event, options, state, node, fetcher, $session }) { /** @type {Record} */ let server_data; @@ -59,251 +47,7 @@ export async function load_node({ event, options, state, route, node, $session } } return $session; }, - /** - * @param {RequestInfo} resource - * @param {RequestInit} opts - */ - fetch: async (resource, opts = {}) => { - /** @type {string} */ - let requested; - - if (typeof resource === 'string') { - requested = resource; - } else { - requested = resource.url; - - opts = { - method: resource.method, - headers: resource.headers, - body: resource.body, - mode: resource.mode, - credentials: resource.credentials, - cache: resource.cache, - redirect: resource.redirect, - referrer: resource.referrer, - integrity: resource.integrity, - ...opts - }; - } - - opts.headers = new Headers(opts.headers); - - // merge headers from request - for (const [key, value] of event.request.headers) { - if ( - key !== 'authorization' && - key !== 'connection' && - key !== 'cookie' && - key !== 'host' && - key !== 'if-none-match' && - !opts.headers.has(key) - ) { - opts.headers.set(key, value); - } - } - - const resolved = resolve(event.url.pathname, requested.split('?')[0]); - - /** @type {Response} */ - let response; - - /** @type {import('types').PrerenderDependency} */ - let dependency; - - // handle fetch requests for static assets. e.g. prebaked data, etc. - // we need to support everything the browser's fetch supports - const prefix = options.paths.assets || options.paths.base; - const filename = decodeURIComponent( - resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved - ).slice(1); - const filename_html = `${filename}/index.html`; // path may also match path/index.html - - const is_asset = options.manifest.assets.has(filename); - const is_asset_html = options.manifest.assets.has(filename_html); - - if (is_asset || is_asset_html) { - const file = is_asset ? filename : filename_html; - - if (options.read) { - const type = is_asset - ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))] - : 'text/html'; - - response = new Response(options.read(file), { - headers: type ? { 'content-type': type } : {} - }); - } else { - response = await fetch( - `${event.url.origin}/${file}`, - /** @type {RequestInit} */ (opts) - ); - } - } else if (is_root_relative(resolved)) { - if (opts.credentials !== 'omit') { - const authorization = event.request.headers.get('authorization'); - - // combine cookies from the initiating request with any that were - // added via set-cookie - const combined_cookies = { ...cookies }; - - for (const cookie of new_cookies) { - if (!domain_matches(event.url.hostname, cookie.domain)) continue; - if (!path_matches(resolved, cookie.path)) continue; - - combined_cookies[cookie.name] = cookie.value; - } - - const cookie = Object.entries(combined_cookies) - .map(([name, value]) => `${name}=${value}`) - .join('; '); - - if (cookie) { - opts.headers.set('cookie', cookie); - } - - if (authorization && !opts.headers.has('authorization')) { - opts.headers.set('authorization', authorization); - } - } - - if (opts.body && typeof opts.body !== 'string') { - // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a - // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object. - // non-string bodies are irksome to deal with, but luckily aren't particularly useful - // in this context anyway, so we take the easy route and ban them - throw new Error('Request body must be a string'); - } - - response = await respond( - new Request(new URL(requested, event.url).href, { ...opts }), - options, - { - ...state, - initiator: route - } - ); - - if (state.prerendering) { - dependency = { response, body: null }; - state.prerendering.dependencies.set(resolved, dependency); - } - } else { - // external - if (resolved.startsWith('//')) { - requested = event.url.protocol + requested; - } - - // external fetch - // allow cookie passthrough for "same-origin" - // if SvelteKit is serving my.domain.com: - // - domain.com WILL NOT receive cookies - // - my.domain.com WILL receive cookies - // - api.domain.dom WILL NOT receive cookies - // - sub.my.domain.com WILL receive cookies - // ports do not affect the resolution - // leading dot prevents mydomain.com matching domain.com - if ( - `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) && - opts.credentials !== 'omit' - ) { - const cookie = event.request.headers.get('cookie'); - if (cookie) opts.headers.set('cookie', cookie); - } - - // we need to delete the connection header, as explained here: - // https://github.com/nodejs/undici/issues/1470#issuecomment-1140798467 - // TODO this may be a case for being selective about which headers we let through - opts.headers.delete('connection'); - - const external_request = new Request(requested, /** @type {RequestInit} */ (opts)); - response = await options.hooks.externalFetch.call(null, external_request); - } - - const set_cookie = response.headers.get('set-cookie'); - if (set_cookie) { - new_cookies.push( - ...set_cookie_parser - .splitCookiesString(set_cookie) - .map((str) => set_cookie_parser.parseString(str)) - ); - } - - const proxy = new Proxy(response, { - get(response, key, _receiver) { - async function text() { - const body = await response.text(); - - /** @type {import('types').ResponseHeaders} */ - const headers = {}; - for (const [key, value] of response.headers) { - // TODO skip others besides set-cookie and etag? - if (key !== 'set-cookie' && key !== 'etag') { - headers[key] = value; - } - } - - if (!opts.body || typeof opts.body === 'string') { - const status_number = Number(response.status); - if (isNaN(status_number)) { - throw new Error( - `response.status is not a number. value: "${ - response.status - }" type: ${typeof response.status}` - ); - } - - fetched.push({ - url: requested, - body: opts.body, - response: { - status: status_number, - statusText: response.statusText, - headers, - body - } - }); - } - - if (dependency) { - dependency.body = body; - } - - return body; - } - - if (key === 'arrayBuffer') { - return async () => { - const buffer = await response.arrayBuffer(); - - if (dependency) { - dependency.body = new Uint8Array(buffer); - } - - // TODO should buffer be inlined into the page (albeit base64'd)? - // any conditions in which it shouldn't be? - - return buffer; - }; - } - - if (key === 'text') { - return text; - } - - if (key === 'json') { - return async () => { - return JSON.parse(await text()); - }; - } - - // TODO arrayBuffer? - - return Reflect.get(response, key, response); - } - }); - - return proxy; - }, + fetch: fetcher, setHeaders: event.setHeaders }; @@ -323,7 +67,6 @@ export async function load_node({ event, options, state, route, node, $session } return { node, data, - server_data, // we return this separately so it can be serialized into the page - fetched + server_data // we return this separately so it can be serialized into the page }; } diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 3d416511599c..1a54d62ca558 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -19,6 +19,7 @@ const updated = { * Creates the HTML response. * @param {{ * branch: Array; + * fetched: Array; * options: import('types').SSROptions; * state: import('types').SSRState; * $session: any; @@ -31,6 +32,7 @@ const updated = { */ export async function render_response({ branch, + fetched, options, state, $session, @@ -71,7 +73,7 @@ export async function render_response({ } if (resolve_opts.ssr) { - for (const { node, fetched } of branch) { + for (const { node } of branch) { if (node.imports) { node.imports.forEach((url) => modulepreloads.add(url)); } @@ -83,10 +85,10 @@ export async function render_response({ if (node.inline_styles) { Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v)); } - - if (fetched && page_config.hydrate) serialized_data.push(...fetched); } + if (fetched && page_config.hydrate) serialized_data.push(...fetched); + const session = writable($session); /** @type {Record} */ 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 c915cb791996..37ad90cd6594 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -2,6 +2,7 @@ import { render_response } from './render.js'; import { load_node } from './load_node.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { GENERIC_ERROR } from '../utils.js'; +import { create_fetch } from './fetch.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -29,6 +30,8 @@ export async function respond_with_error({ error, resolve_opts }) { + const { fetcher, fetched } = create_fetch({ event, options, state, route: GENERIC_ERROR }); + try { const branch = []; @@ -41,17 +44,16 @@ export async function respond_with_error({ event, options, state, - route: GENERIC_ERROR, node: default_layout, - $session + $session, + fetcher }) ); const error_loaded = /** @type {Loaded} */ ({ node: default_error, data: {}, - server_data: {}, - fetched: [] + server_data: {} }); branch.push(layout_loaded, error_loaded); @@ -68,6 +70,7 @@ export async function respond_with_error({ status, error, branch, + fetched, event, resolve_opts }); diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 8f58706e8e0c..8177ae16316b 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -1,6 +1,6 @@ import { JSONValue, ResponseHeaders, SSRNode, CspDirectives } from 'types'; -export type Fetched = { +export interface Fetched { url: string; body?: string | null; response: { @@ -9,13 +9,18 @@ export type Fetched = { headers: ResponseHeaders; body: string; }; -}; +} + +export interface FetchState { + fetched: Fetched[]; + cookies: string[]; + new_cookies: string[]; +} export type Loaded = { node: SSRNode; data: Record; server_data: JSONValue; - fetched: Fetched[]; }; type CspMode = 'hash' | 'nonce' | 'auto'; From ddeb2ffae02394a3820acd897769d403cd45d241 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 19:56:37 -0400 Subject: [PATCH 068/110] simplify --- packages/kit/src/core/sync/write_root.js | 8 ++++---- packages/kit/src/runtime/client/client.js | 3 +-- packages/kit/src/runtime/server/page/render.js | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/kit/src/core/sync/write_root.js b/packages/kit/src/core/sync/write_root.js index 96e98a0e5c65..a5bc8506803e 100644 --- a/packages/kit/src/core/sync/write_root.js +++ b/packages/kit/src/core/sync/write_root.js @@ -21,16 +21,16 @@ export function write_root(manifest_data, output) { let l = max_depth; - let pyramid = ``; + let pyramid = ``; while (l--) { pyramid = ` {#if components[${l + 1}]} - + ${pyramid.replace(/\n/g, '\n\t\t\t\t\t')} {:else} - + {/if} ` .replace(/^\t\t\t/gm, '') @@ -49,7 +49,7 @@ export function write_root(manifest_data, output) { export let page; export let components; - ${levels.map((l) => `export let props_${l} = null;`).join('\n\t\t\t\t')} + ${levels.map((l) => `export let data_${l} = null;`).join('\n\t\t\t\t')} setContext('__svelte__', stores); diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index ac700963338a..aa2009f05a06 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -424,8 +424,7 @@ export function create_client({ target, session, base, trailing_slash }) { for (let i = 0; i < filtered.length; i += 1) { // Only set props if the node actually updated. This prevents needless rerenders. if (!current.branch.some((node) => node === filtered[i])) { - const data = filtered[i].data; - result.props[`props_${i}`] = { data }; + result.props[`data_${i}`] = filtered[i].data; } } diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 1a54d62ca558..009bdcba1499 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -130,7 +130,7 @@ export async function render_response({ // props_n (instead of props[n]) makes it easy to avoid // unnecessary updates for layout components for (let i = 0; i < branch.length; i += 1) { - props[`props_${i}`] = { data: branch[i].data }; + props[`data_${i}`] = branch[i].data; } rendered = options.root.render(props); From 3efbd5612ed9ea6ff0853efd41ba9837b1464278 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 20:03:46 -0400 Subject: [PATCH 069/110] rename to disambiguate with client --- packages/kit/src/runtime/server/index.js | 1 + packages/kit/src/runtime/server/page/index.js | 4 ++-- .../server/page/{load_node.js => load_data.js} | 11 +---------- .../kit/src/runtime/server/page/respond_with_error.js | 4 ++-- 4 files changed, 6 insertions(+), 14 deletions(-) rename packages/kit/src/runtime/server/page/{load_node.js => load_data.js} (85%) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index f7e0dd166bd1..6b3acb0b1905 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -218,6 +218,7 @@ export async function respond(request, options, state) { status: 200, error: null, branch: [], + fetched: [], resolve_opts: { ...resolve_opts, ssr: false diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index cc1f767069ec..e3d0dfa58b0c 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -1,6 +1,6 @@ import { negotiate } from '../../../utils/http.js'; import { render_response } from './render.js'; -import { load_node } from './load_node.js'; +import { load_data } from './load_data.js'; import { respond_with_error } from './respond_with_error.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { method_not_allowed } from '../utils.js'; @@ -101,7 +101,7 @@ export async function render_page(event, route, options, state, resolve_opts) { if (node) { try { - const loaded = await load_node({ + const loaded = await load_data({ event, options, state, diff --git a/packages/kit/src/runtime/server/page/load_node.js b/packages/kit/src/runtime/server/page/load_data.js similarity index 85% rename from packages/kit/src/runtime/server/page/load_node.js rename to packages/kit/src/runtime/server/page/load_data.js index 50b838a2e6b5..a1f13c634823 100644 --- a/packages/kit/src/runtime/server/page/load_node.js +++ b/packages/kit/src/runtime/server/page/load_data.js @@ -12,7 +12,7 @@ import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; * }} opts * @returns {Promise} */ -export async function load_node({ event, options, state, node, fetcher, $session }) { +export async function load_data({ event, options, state, node, fetcher, $session }) { /** @type {Record} */ let server_data; @@ -51,15 +51,6 @@ export async function load_node({ event, options, state, node, fetcher, $session setHeaders: event.setHeaders }; - if (options.dev) { - // TODO remove this for 1.0 - Object.defineProperty(load_event, 'page', { - get: () => { - throw new Error('`page` in `load` functions has been replaced by `url` and `params`'); - } - }); - } - // TODO unwrap top-level promises data = await node.module.load.call(null, load_event); } 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 37ad90cd6594..b2555312998e 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -1,5 +1,5 @@ import { render_response } from './render.js'; -import { load_node } from './load_node.js'; +import { load_data } from './load_data.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { GENERIC_ERROR } from '../utils.js'; import { create_fetch } from './fetch.js'; @@ -40,7 +40,7 @@ export async function respond_with_error({ const default_error = await options.manifest._.nodes[1](); // 1 is always the root error const layout_loaded = /** @type {Loaded} */ ( - await load_node({ + await load_data({ event, options, state, From 8631384a5f9eff432c6a067cf2dc95fc7037cdab Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 20:46:47 -0400 Subject: [PATCH 070/110] tweaks --- packages/kit/src/runtime/server/page/index.js | 5 +++++ .../kit/src/runtime/server/page/load_data.js | 20 +++---------------- .../kit/src/runtime/server/page/types.d.ts | 2 +- packages/kit/types/index.d.ts | 5 +++-- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index e3d0dfa58b0c..27a2ae2041a4 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -88,6 +88,11 @@ export async function render_page(event, route, options, state, resolve_opts) { status: 204 }); } + + const mod = leaf_node.server; + if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { + throw new Error('Cannot prerender pages that have endpoints with mutative methods'); + } } /** @type {Array} */ diff --git a/packages/kit/src/runtime/server/page/load_data.js b/packages/kit/src/runtime/server/page/load_data.js index a1f13c634823..fccde0882340 100644 --- a/packages/kit/src/runtime/server/page/load_data.js +++ b/packages/kit/src/runtime/server/page/load_data.js @@ -13,22 +13,8 @@ import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; * @returns {Promise} */ export async function load_data({ event, options, state, node, fetcher, $session }) { - /** @type {Record} */ - let server_data; - - if (node.server) { - const should_prerender = node.module?.prerender ?? options.prerender.default; - const mod = node.server; - - if (should_prerender && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { - throw new Error('Cannot prerender pages that have endpoints with mutative methods'); - } - - // TODO unwrap top-level promises - server_data = await mod.GET.call(null, event); - } else { - server_data = {}; - } + /** @type {Record | null} */ + const server_data = node.server?.GET?.call(null, event) ?? null; // TODO unwrap top-level promises let data = server_data; @@ -52,7 +38,7 @@ export async function load_data({ event, options, state, node, fetcher, $session }; // TODO unwrap top-level promises - data = await node.module.load.call(null, load_event); + data = (await node.module.load.call(null, load_event)) ?? null; } return { diff --git a/packages/kit/src/runtime/server/page/types.d.ts b/packages/kit/src/runtime/server/page/types.d.ts index 8177ae16316b..211641c2d3a1 100644 --- a/packages/kit/src/runtime/server/page/types.d.ts +++ b/packages/kit/src/runtime/server/page/types.d.ts @@ -19,7 +19,7 @@ export interface FetchState { export type Loaded = { node: SSRNode; - data: Record; + data: Record | null; server_data: JSONValue; }; diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 9d7323d7a960..495e32f45115 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -187,7 +187,7 @@ export interface Load< export interface LoadEvent< Params extends Record = Record, - Data extends Record = Record + Data extends Record | null = Record | null > { fetch(info: RequestInfo, init?: RequestInit): Promise; params: Params; @@ -196,7 +196,8 @@ export interface LoadEvent< session: App.Session; setHeaders: (headers: ResponseHeaders) => void; url: URL; - // TODO parent, depends + parent: () => Promise>; + depends: (...deps: string[]) => void; } export interface LoadOutputCache { From 83d46006cfb238c56bbe9f285d8bc49214fb0324 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 20:51:32 -0400 Subject: [PATCH 071/110] tidy up --- .../runtime/server/page/respond_with_error.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) 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 b2555312998e..5d429d853b5c 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -39,16 +39,14 @@ export async function respond_with_error({ const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout const default_error = await options.manifest._.nodes[1](); // 1 is always the root error - const layout_loaded = /** @type {Loaded} */ ( - await load_data({ - event, - options, - state, - node: default_layout, - $session, - fetcher - }) - ); + const layout_loaded = await load_data({ + event, + options, + state, + node: default_layout, + $session, + fetcher + }); const error_loaded = /** @type {Loaded} */ ({ node: default_error, From bcaef095bfa7683f74b8e7bb91406d7011ca216c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 4 Aug 2022 22:35:57 -0400 Subject: [PATCH 072/110] load data in parallel --- packages/kit/src/index/private.js | 8 + packages/kit/src/runtime/server/page/index.js | 170 +++++++++++++----- .../runtime/server/page/respond_with_error.js | 34 ++-- packages/kit/src/runtime/server/utils.js | 2 +- .../kit/test/apps/basics/test/server.test.js | 12 +- packages/kit/types/index.d.ts | 2 +- 6 files changed, 150 insertions(+), 78 deletions(-) diff --git a/packages/kit/src/index/private.js b/packages/kit/src/index/private.js index 91c74f708cc1..a31e2f3eafb4 100644 --- a/packages/kit/src/index/private.js +++ b/packages/kit/src/index/private.js @@ -6,6 +6,10 @@ export class HttpError { constructor(status, message) { this.status = status; this.message = message; + + // this is a hack to workaround failed instanceof checks + // TODO figure out a better way to do this + this.__is_http_error = true; } } @@ -17,5 +21,9 @@ export class Redirect { constructor(status, location) { this.status = status; this.location = location; + + // this is a hack to workaround failed instanceof checks + // TODO figure out a better way to do this + this.__is_redirect = true; } } diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 27a2ae2041a4..1ae281d71f9b 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -1,11 +1,11 @@ import { negotiate } from '../../../utils/http.js'; import { render_response } from './render.js'; -import { load_data } from './load_data.js'; import { respond_with_error } from './respond_with_error.js'; import { coalesce_to_error } from '../../../utils/error.js'; -import { method_not_allowed } from '../utils.js'; -import { Redirect } from '../../../index/private.js'; +import { method_not_allowed, clone_error } from '../utils.js'; +import { HttpError, Redirect } from '../../../index/private.js'; import { create_fetch } from './fetch.js'; +import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -38,7 +38,7 @@ export async function render_page(event, route, options, state, resolve_opts) { if (accept === 'application/json') { const node = await options.manifest._.nodes[route.page](); if (node.server) { - return handle_json_request(event, node.server); + return handle_json_request(event, options, node.server); } } @@ -46,27 +46,6 @@ export async function render_page(event, route, options, state, resolve_opts) { const { fetcher, fetched } = create_fetch({ event, options, state, route }); - // TODO for non-GET requests, first call handler in +page.server.js - // (this also determines status code) - - if (!resolve_opts.ssr) { - return await render_response({ - branch: [], - fetched, - page_config: { - hydrate: true, - router: true - }, - status: 200, - error: null, - event, - options, - state, - $session, - resolve_opts - }); - } - try { const nodes = await Promise.all([ // we use == here rather than === because [undefined] serializes as "[null]" @@ -74,9 +53,27 @@ export async function render_page(event, route, options, state, resolve_opts) { options.manifest._.nodes[route.page]() ]); + // TODO for non-GET requests, first call handler in +page.server.js + // (this also determines status code) const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1)); - let page_config = get_page_config(leaf_node, options); + if (!resolve_opts.ssr) { + return await render_response({ + branch: [], + fetched, + page_config: { + hydrate: true, + router: true + }, + status: 200, + error: null, + event, + options, + state, + $session, + resolve_opts + }); + } if (state.prerendering) { // if the page isn't marked as prerenderable (or is explicitly @@ -101,21 +98,91 @@ export async function render_page(event, route, options, state, resolve_opts) { /** @type {number} */ let status = 200; + /** @type {Error | null} */ + let error = null; + + /** @type {Array>} */ + const server_promises = nodes.map((node, i) => { + if (error) throw error; // if an error happens immediately, don't bother with the rest of the nodes + return Promise.resolve().then(async () => { + try { + const server_data = node?.server?.GET?.call(null, { + ...event, + parent: async () => { + const data = {}; + for (let j = 0; j < i - 1; j += 1) { + Object.assign(data, await server_promises[j]); + } + return data; + } + }); + + return server_data ? unwrap_promises(server_data) : null; + } catch (e) { + error = /** @type {Error} */ (e); + throw error; + } + }); + }); + + /** @type {Array | null>>} */ + const load_promises = nodes.map((node, i) => { + if (error) throw error; + return Promise.resolve().then(async () => { + try { + const server_data = await server_promises[i]; + + if (node?.module?.load) { + const data = await node?.module?.load?.call(null, { + url: state.prerendering ? new PrerenderingURL(event.url) : new LoadURL(event.url), + params: event.params, + data: server_data, + routeId: event.routeId, + get session() { + if (node.module.prerender ?? options.prerender.default) { + throw Error( + 'Attempted to access session from a prerendered page. Session would never be populated.' + ); + } + return $session; + }, + fetch: fetcher, + setHeaders: event.setHeaders, + depends: () => {}, + parent: async () => { + const data = {}; + for (let j = 0; j < i - 1; j += 1) { + Object.assign(data, await load_promises[j]); + } + return data; + } + }); + + return data ? unwrap_promises(data) : null; + } + + return server_data; + } catch (e) { + error = /** @type {Error} */ (e); + throw error; + } + }); + }); + + // if we don't do this, rejections will be unhandled + for (const p of server_promises) p.catch(() => {}); + for (const p of load_promises) p.catch(() => {}); + for (let i = 0; i < nodes.length; i += 1) { const node = nodes[i]; if (node) { try { - const loaded = await load_data({ - event, - options, - state, - $session, + branch.push({ node, - fetcher + server_data: await server_promises[i], + data: await load_promises[i] }); - - branch.push(loaded); } catch (err) { const error = coalesce_to_error(err); @@ -126,17 +193,11 @@ export async function render_page(event, route, options, state, resolve_opts) { while (i--) { if (route.errors[i]) { const index = /** @type {number} */ (route.errors[i]); - const error_node = await options.manifest._.nodes[index](); + const node = await options.manifest._.nodes[index](); let j = i; while (!branch[j]) j -= 1; - const error_loaded = /** @type {import('./types').Loaded} */ ({ - node: error_node, - data: {}, - server_data: {} - }); - return await render_response({ event, options, @@ -149,7 +210,7 @@ export async function render_page(event, route, options, state, resolve_opts) { branch: branch .slice(0, j + 1) .filter(Boolean) - .concat(error_loaded), + .concat({ node, data: null, server_data: null }), fetched }); } @@ -168,7 +229,7 @@ export async function render_page(event, route, options, state, resolve_opts) { state, $session, resolve_opts, - page_config, + page_config: get_page_config(leaf_node, options), status, error: null, branch: branch.filter(Boolean), @@ -211,9 +272,10 @@ function get_page_config(leaf, options) { /** * @param {import('types').RequestEvent} event + * @param {import('types').SSROptions} options * @param {import('types').SSRNode['server']} mod */ -async function handle_json_request(event, mod) { +async function handle_json_request(event, options, mod) { const method = /** @type {import('types').HttpMethod} */ (event.request.method); const handler = mod[method === 'HEAD' ? 'GET' : method]; @@ -245,11 +307,15 @@ async function handle_json_request(event, mod) { return new Response(undefined, { status: 204 }); } catch (error) { - if (error instanceof Redirect) { + if (error?.__is_redirect) { return Response.redirect(error.location, error.status); } - return json_response({ message: error.message }, error.status || 500); + if (error?.__is_http_error) { + return json_response({ message: error.message }, error.status); + } + + return json_response(clone_error(error, options.get_stack), 500); } } @@ -266,3 +332,15 @@ function json_response(data, status) { } }); } + +/** @param {Record} object */ +async function unwrap_promises(object) { + /** @type {import('types').JSONObject} */ + const unwrapped = {}; + + for (const key in object) { + unwrapped[key] = await object[key]; + } + + return unwrapped; +} 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 5d429d853b5c..ac30a5c2921c 100644 --- a/packages/kit/src/runtime/server/page/respond_with_error.js +++ b/packages/kit/src/runtime/server/page/respond_with_error.js @@ -36,25 +36,21 @@ export async function respond_with_error({ const branch = []; if (resolve_opts.ssr) { - const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout - const default_error = await options.manifest._.nodes[1](); // 1 is always the root error - - const layout_loaded = await load_data({ - event, - options, - state, - node: default_layout, - $session, - fetcher - }); - - const error_loaded = /** @type {Loaded} */ ({ - node: default_error, - data: {}, - server_data: {} - }); - - branch.push(layout_loaded, error_loaded); + branch.push( + await load_data({ + event, + options, + state, + node: await options.manifest._.nodes[0](), // 0 is always the root layout + $session, + fetcher + }), + { + node: await options.manifest._.nodes[1](), // 1 is always the root error + data: null, + server_data: null + } + ); } return await render_response({ diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index 7b7ff7e83248..d7180e702ae6 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -32,7 +32,7 @@ export function serialize_error(error, get_stack) { * @param {Error} error * @param {(error: Error) => string | undefined} get_stack */ -function clone_error(error, get_stack) { +export function clone_error(error, get_stack) { const { name, message, diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index a9bb172341a1..21782b1006db 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -109,9 +109,6 @@ test.describe('Errors', () => { ); }); - // TODO before we implemented route fallthroughs, and there was a 1:1 - // regex:route relationship, it was simple to say 'method not implemented - // for this endpoint'. now it's a little tricker. does a 404 suffice? test('unhandled http method', async ({ request }) => { const response = await request.put('/errors/invalid-route-response'); @@ -155,17 +152,10 @@ test.describe('Errors', () => { } }); - const { message, name, stack } = await response.json(); + const { message } = await response.json(); expect(response.status()).toBe(400); - expect(name).toBe('Error'); expect(message).toBe('oops'); - - if (process.env.DEV) { - expect(stack.split('\n').length).toBeGreaterThan(1); - } else { - expect(stack.split('\n').length).toBe(1); - } }); test('returns 400 when accessing a malformed URI', async ({ page }) => { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index 495e32f45115..e9fbb96324c8 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -191,7 +191,7 @@ export interface LoadEvent< > { fetch(info: RequestInfo, init?: RequestInit): Promise; params: Params; - data: Data; + data: JSONObject | null; routeId: string | null; session: App.Session; setHeaders: (headers: ResponseHeaders) => void; From 78d6ab7bb4cf6ab7f51724c336edac98d2219032 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 5 Aug 2022 14:30:29 +0200 Subject: [PATCH 073/110] throw error pointing to migration if router folder exists but no routes are found --- packages/kit/src/core/sync/create_manifest_data/index.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index c31ee4291096..05d41d9a08d9 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -192,6 +192,13 @@ export default function create_manifest_data({ } } }); + + // TODO remove for 1.0 + if (route_map.size === 0) { + throw new Error( + 'The filesystem router API has changed, see https://github.com/sveltejs/kit/discussions/5774 for details' + ); + } } /** @type {import('types').PageNode[]} */ From e83e2d2de07bdf9d68fd5ffc8f0d0673ccbd6564 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 5 Aug 2022 16:49:05 +0200 Subject: [PATCH 074/110] load nodes on client in parallel --- packages/kit/src/runtime/client/client.js | 225 +++++++++++++-------- packages/kit/src/runtime/client/types.d.ts | 1 + 2 files changed, 137 insertions(+), 89 deletions(-) diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index aa2009f05a06..1baa1a4a2918 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -457,20 +457,22 @@ export function create_client({ target, session, base, trailing_slash }) { /** * @param {{ * node: import('types').CSRPageNode; + * parent: () => Promise>; * url: URL; * params: Record; * routeId: string | null; * }} options * @returns {Promise} */ - async function load_node({ node, url, params, routeId }) { + async function load_node({ node, parent, url, params, routeId }) { // TODO loading from node.server belongs in here, not in load_route etc const uses = { params: new Set(), url: false, session: false, - dependencies: new Set() + dependencies: new Set(), + parent: false }; /** @param dep {string} */ @@ -564,8 +566,11 @@ export function create_client({ target, session, base, trailing_slash }) { return started ? native_fetch(normalized, init) : initial_fetch(requested, init); }, setHeaders: () => {}, // noop - depends - // TODO parent + depends, + parent: () => { + uses.parent = true; + return parent(); + } }; if (import.meta.env.DEV) { @@ -619,95 +624,124 @@ export function create_client({ target, session, base, trailing_slash }) { session: session_id !== current.session_id }; - /** @type {Array} */ - const branch = []; - // preload modules to avoid waterfall, but handle rejections // so they don't get reported to Sentry et al (we don't need // to act on the failures at this point) [...errors, ...layouts, page].forEach((loader) => loader?.().catch(() => {})); const nodes = [...layouts, page]; + let parent_changed_since_last_render = false; - for (let i = 0; i < nodes.length; i += 1) { - /** @type {import('./types').BranchNode | undefined} */ - let branch_node; + const branch_promises = nodes.map(async (loader, i) => { + return Promise.resolve().then(async () => { + if (!loader) return; + try { + const node = await loader(); + /** @type {import('./types').BranchNode | undefined} */ + const previous = current.branch[i]; + + let changed_since_last_render = + !previous || + node !== previous.node || + (changed.url && previous.uses.url) || + changed.params.some((param) => previous.uses.params.has(param)) || + (changed.session && previous.uses.session) || + Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))); + const parent = async () => { + const data = {}; + for (let j = 0; j < i - 1; j += 1) { + Object.assign(data, await branch_promises[j]); + } + return data; + }; + if (previous?.uses.parent) { + // Unfortunate: This reintroduces a potential waterfall if people don't have `await parent()` + // as their first await call in their load function. + await parent(); + changed_since_last_render = parent_changed_since_last_render; + } + parent_changed_since_last_render ||= changed_since_last_render; + + if (changed_since_last_render) { + return await load_node({ + node, + url, + params, + routeId: route.id, + parent + }); + } else { + return previous; + } + } catch (e) {} + }); + }); - try { - if (!nodes[i]) continue; - - const node = await nodes[i](); - const previous = current.branch[i]; - - const changed_since_last_render = - !previous || - node !== previous.node || - (changed.url && previous.uses.url) || - changed.params.some((param) => previous.uses.params.has(param)) || - (changed.session && previous.uses.session) || - Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))); - - if (changed_since_last_render) { - branch_node = await load_node({ - node, - url, - params, - routeId: route.id - }); - } else { - branch_node = previous; - } + // if we don't do this, rejections will be unhandled + for (const p of branch_promises) p.catch(() => {}); - branch.push(branch_node); - } catch (e) { - if (e instanceof Redirect) { - // TODO handle redirect - } + /** @type {Array} */ + const branch = []; + + for (let i = 0; i < nodes.length; i += 1) { + if (nodes[i]) { + try { + branch.push(await branch_promises[i]); + } catch (e) { + if (e instanceof Redirect) { + // TODO handle redirect + } - const status = e instanceof HttpError ? e.status : 500; - const error = coalesce_to_error(e); - - while (i--) { - if (errors[i]) { - /** @type {import('./types').BranchNode | undefined} */ - let error_loaded; - - let j = i; - while (!branch[j]) j -= 1; - - try { - error_loaded = { - node: await errors[i](), - data: {}, - uses: { - params: new Set(), - url: false, - session: false, - dependencies: new Set() - }, - redirect: undefined - }; - - return await get_navigation_result_from_branch({ - url, - params, - branch: branch.slice(0, j + 1).concat(error_loaded), - status, - error, - routeId: route.id - }); - } catch (e) { - continue; + const status = e instanceof HttpError ? e.status : 500; + const error = coalesce_to_error(e); + + while (i--) { + if (errors[i]) { + /** @type {import('./types').BranchNode | undefined} */ + let error_loaded; + + let j = i; + while (!branch[j]) j -= 1; + + try { + error_loaded = { + node: await errors[i](), + data: {}, + uses: { + params: new Set(), + url: false, + session: false, + dependencies: new Set(), + parent: false + }, + redirect: undefined + }; + + return await get_navigation_result_from_branch({ + url, + params, + branch: branch.slice(0, j + 1).concat(error_loaded), + status, + error, + routeId: route.id + }); + } catch (e) { + continue; + } } } - } - return await load_root_error_page({ - status, - error, - url, - routeId: route.id - }); + return await load_root_error_page({ + status, + error, + url, + routeId: route.id + }); + } + } else { + // push an empty slot so we can rewind past gaps to the + // layout that corresponds with an +error.svelte page + branch.push(undefined); } } @@ -737,14 +771,16 @@ export function create_client({ target, session, base, trailing_slash }) { node: await default_layout, url, params, - routeId + routeId, + parent: () => Promise.resolve({}) }); const root_error = await load_node({ node: await default_error, url, params, - routeId + routeId, + parent: () => Promise.resolve({}) }); return await get_navigation_result_from_branch({ @@ -1121,7 +1157,7 @@ export function create_client({ target, session, base, trailing_slash }) { _hydrate: async ({ status, error, node_ids, params, routeId }) => { const url = new URL(location.href); - /** @type {Array} */ + /** @type {Array>} */ const branch = []; /** @type {import('./types').NavigationResult | undefined} */ @@ -1131,11 +1167,22 @@ export function create_client({ target, session, base, trailing_slash }) { for (let i = 0; i < node_ids.length; i += 1) { const node_id = node_ids[i]; - const branch_node = await load_node({ - node: await nodes[node_id](), - url, - params, - routeId + const branch_node = Promise.resolve().then(async () => { + return load_node({ + node: await nodes[node_id](), + url, + params, + routeId, + parent: async () => { + const data = {}; + for (let j = 0; j < i - 1; j += 1) { + Object.assign(data, await branch[j]); + } + return data; + } + // TODO pass deserialized server data + // TODO ..but in a way that each node only gets its own data? was that the case before? + }); }); branch.push(branch_node); @@ -1144,7 +1191,7 @@ export function create_client({ target, session, base, trailing_slash }) { result = await get_navigation_result_from_branch({ url, params, - branch, + branch: await Promise.all(branch), status, error, routeId diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index 5be69f75f66a..25db994e8c18 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -62,6 +62,7 @@ export type BranchNode = { url: boolean; // TODO make more granular? session: boolean; dependencies: Set; + parent: boolean; }; redirect: string | undefined; }; From 6f5b7e0296966bf71f0b70395bcd69f5bd9281a0 Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 5 Aug 2022 17:28:33 +0200 Subject: [PATCH 075/110] handle redirect --- packages/kit/src/runtime/client/client.js | 41 +++++++++++----------- packages/kit/src/runtime/client/types.d.ts | 10 ++++-- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index 1baa1a4a2918..e791132b6a2e 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -258,12 +258,12 @@ export function create_client({ target, session, base, trailing_slash }) { }); } else { if (router_enabled) { - goto(new URL(navigation_result.redirect, url).href, {}, [ + goto(new URL(navigation_result.location, url).href, {}, [ ...redirect_chain, url.pathname ]); } else { - await native_navigation(new URL(navigation_result.redirect, location.href)); + await native_navigation(new URL(navigation_result.location, location.href)); } return false; @@ -361,7 +361,7 @@ export function create_client({ target, session, base, trailing_slash }) { updating = false; } - /** @param {import('./types').NavigationResult} result */ + /** @param {import('./types').NavigationFinishedResult} result */ function initialize(result) { current = result.state; @@ -404,11 +404,9 @@ export function create_client({ target, session, base, trailing_slash }) { routeId }) { const filtered = /** @type {import('./types').BranchNode[] } */ (branch.filter(Boolean)); - const redirect = filtered.find((f) => f.redirect); - /** @type {import('./types').NavigationResult} */ + /** @type {import('./types').NavigationFinishedResult} */ const result = { - redirect: redirect?.redirect, state: { url, params, @@ -597,14 +595,14 @@ export function create_client({ target, session, base, trailing_slash }) { return { node, data: data || {}, - uses, - redirect: undefined + uses }; } /** * @param {import('./types').NavigationIntent} intent * @param {boolean} no_cache + * @returns {Promise} */ async function load_route({ id, url, params, route }, no_cache) { if (load_cache.id === id && load_cache.promise) { @@ -689,7 +687,10 @@ export function create_client({ target, session, base, trailing_slash }) { branch.push(await branch_promises[i]); } catch (e) { if (e instanceof Redirect) { - // TODO handle redirect + return { + redirect: true, + location: e.location + }; } const status = e instanceof HttpError ? e.status : 500; @@ -713,8 +714,7 @@ export function create_client({ target, session, base, trailing_slash }) { session: false, dependencies: new Set(), parent: false - }, - redirect: undefined + } }; return await get_navigation_result_from_branch({ @@ -1160,7 +1160,7 @@ export function create_client({ target, session, base, trailing_slash }) { /** @type {Array>} */ const branch = []; - /** @type {import('./types').NavigationResult | undefined} */ + /** @type {import('./types').NavigationFinishedResult | undefined} */ let result; try { @@ -1197,8 +1197,15 @@ export function create_client({ target, session, base, trailing_slash }) { routeId }); } catch (e) { - // TODO handle HttpError and Redirect cases - if (error) throw e; + // TODO handle HttpError cases + // TODO order of these ifs sensible? + if (e instanceof Redirect) { + // this is a real edge case — `load` would need to return + // a redirect but only in the browser + await native_navigation(new URL(e.location, location.href)); + } else if (error) { + throw e; + } result = await load_root_error_page({ status: 500, @@ -1208,12 +1215,6 @@ export function create_client({ target, session, base, trailing_slash }) { }); } - if (result.redirect) { - // this is a real edge case — `load` would need to return - // a redirect but only in the browser - await native_navigation(new URL(result.redirect, location.href)); - } - initialize(result); } }; diff --git a/packages/kit/src/runtime/client/types.d.ts b/packages/kit/src/runtime/client/types.d.ts index 25db994e8c18..203c55851097 100644 --- a/packages/kit/src/runtime/client/types.d.ts +++ b/packages/kit/src/runtime/client/types.d.ts @@ -48,8 +48,13 @@ export type NavigationIntent = { url: URL; }; -export type NavigationResult = { - redirect?: string; +export type NavigationResult = NavigationRedirectResult | NavigationFinishedResult; +export type NavigationRedirectResult = { + redirect: true; + location: string; +}; +export type NavigationFinishedResult = { + redirect?: false; state: NavigationState; props: Record; }; @@ -64,7 +69,6 @@ export type BranchNode = { dependencies: Set; parent: boolean; }; - redirect: string | undefined; }; export type NavigationState = { From 883fa8b6fa6b513e183598225990afdd55e047dd Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Fri, 5 Aug 2022 18:02:54 +0200 Subject: [PATCH 076/110] fix parent usage bug, avoid waterfall when awaiting parents, remove try-catch which would make subsequent catch never getting called --- packages/kit/src/runtime/client/client.js | 86 ++++++++++++----------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index e791132b6a2e..ba44d6504918 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -565,9 +565,11 @@ export function create_client({ target, session, base, trailing_slash }) { }, setHeaders: () => {}, // noop depends, - parent: () => { + get parent() { + // uses.parent assignment here, not on method inokation, else we wouldn't notice when someone + // does await parent() inside an if branch which wasn't executed yet. uses.parent = true; - return parent(); + return parent; } }; @@ -628,50 +630,52 @@ export function create_client({ target, session, base, trailing_slash }) { [...errors, ...layouts, page].forEach((loader) => loader?.().catch(() => {})); const nodes = [...layouts, page]; - let parent_changed_since_last_render = false; + + // To avoid waterfalls when someone awaits a parent, compute as much as possible here already + /** @type{boolean[]} */ + const nodes_changed_since_last_render = []; + for (let i = 0; i < nodes.length; i++) { + if (!nodes[i]) { + nodes_changed_since_last_render.push(false); + } else { + const previous = current.branch[i]; + const changed_since_last_render = + !previous || + (changed.url && previous.uses.url) || + changed.params.some((param) => previous.uses.params.has(param)) || + (changed.session && previous.uses.session) || + Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))) || + (previous.uses.parent && nodes_changed_since_last_render.includes(true)); + nodes_changed_since_last_render.push(changed_since_last_render); + } + } const branch_promises = nodes.map(async (loader, i) => { return Promise.resolve().then(async () => { if (!loader) return; - try { - const node = await loader(); - /** @type {import('./types').BranchNode | undefined} */ - const previous = current.branch[i]; - - let changed_since_last_render = - !previous || - node !== previous.node || - (changed.url && previous.uses.url) || - changed.params.some((param) => previous.uses.params.has(param)) || - (changed.session && previous.uses.session) || - Array.from(previous.uses.dependencies).some((dep) => invalidated.some((fn) => fn(dep))); - const parent = async () => { - const data = {}; - for (let j = 0; j < i - 1; j += 1) { - Object.assign(data, await branch_promises[j]); + const node = await loader(); + /** @type {import('./types').BranchNode | undefined} */ + const previous = current.branch[i]; + const changed_since_last_render = + nodes_changed_since_last_render[i] || !previous || node !== previous.node; + + if (changed_since_last_render) { + return await load_node({ + node, + url, + params, + routeId: route.id, + parent: async () => { + const data = {}; + for (let j = 0; j < i - 1; j += 1) { + Object.assign(data, await branch_promises[j]); + } + return data; } - return data; - }; - if (previous?.uses.parent) { - // Unfortunate: This reintroduces a potential waterfall if people don't have `await parent()` - // as their first await call in their load function. - await parent(); - changed_since_last_render = parent_changed_since_last_render; - } - parent_changed_since_last_render ||= changed_since_last_render; - - if (changed_since_last_render) { - return await load_node({ - node, - url, - params, - routeId: route.id, - parent - }); - } else { - return previous; - } - } catch (e) {} + }); + } else { + return previous; + } }); }); From 628a8f8a9e7c8baa791293331d61bc416a556aed Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 5 Aug 2022 13:04:41 -0400 Subject: [PATCH 077/110] error handling --- packages/kit/src/index/private.js | 6 +++- packages/kit/src/runtime/client/client.js | 4 +-- packages/kit/src/runtime/server/page/index.js | 31 ++++++++++++------- 3 files changed, 26 insertions(+), 15 deletions(-) diff --git a/packages/kit/src/index/private.js b/packages/kit/src/index/private.js index a31e2f3eafb4..039d45bf3d8d 100644 --- a/packages/kit/src/index/private.js +++ b/packages/kit/src/index/private.js @@ -5,12 +5,16 @@ export class HttpError { */ constructor(status, message) { this.status = status; - this.message = message; + this.message = message || `Error: ${status}`; // this is a hack to workaround failed instanceof checks // TODO figure out a better way to do this this.__is_http_error = true; } + + toString() { + return this.message; + } } export class Redirect { diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js index aa2009f05a06..6b572825d8b0 100644 --- a/packages/kit/src/runtime/client/client.js +++ b/packages/kit/src/runtime/client/client.js @@ -660,11 +660,11 @@ export function create_client({ target, session, base, trailing_slash }) { branch.push(branch_node); } catch (e) { - if (e instanceof Redirect) { + if (e?.__is_redirect instanceof Redirect) { // TODO handle redirect } - const status = e instanceof HttpError ? e.status : 500; + const status = e?.__is_http_error ? e.status : 500; const error = coalesce_to_error(e); while (i--) { diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 1ae281d71f9b..481084e73eb8 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -3,9 +3,9 @@ import { render_response } from './render.js'; import { respond_with_error } from './respond_with_error.js'; import { coalesce_to_error } from '../../../utils/error.js'; import { method_not_allowed, clone_error } from '../utils.js'; -import { HttpError, Redirect } from '../../../index/private.js'; import { create_fetch } from './fetch.js'; import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; +import { Redirect } from '../../../index/private.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -95,9 +95,6 @@ export async function render_page(event, route, options, state, resolve_opts) { /** @type {Array} */ let branch = []; - /** @type {number} */ - let status = 200; - /** @type {Error | null} */ let error = null; @@ -183,12 +180,16 @@ export async function render_page(event, route, options, state, resolve_opts) { server_data: await server_promises[i], data: await load_promises[i] }); - } catch (err) { - const error = coalesce_to_error(err); + } catch (error) { + if (/** @type {Redirect} */ (error).__is_redirect) { + return Response.redirect(error.location, error.status); + } - options.handle_error(error, event); + if (!error.__is_http_error) { + options.handle_error(error, event); + } - status = 500; + const status = error.__is_http_error ? error.status : 500; while (i--) { if (route.errors[i]) { @@ -215,6 +216,12 @@ export async function render_page(event, route, options, state, resolve_opts) { }); } } + + // if we're still here, it means the error happened in the root layout, + // which means we have to fall back to a plain text response + // TODO since the requester is expecting HTML, maybe it makes sense to + // doll this up a bit + return new Response(options.get_stack(error) || error.message, { status }); } } else { // push an empty slot so we can rewind past gaps to the @@ -230,14 +237,14 @@ export async function render_page(event, route, options, state, resolve_opts) { $session, resolve_opts, page_config: get_page_config(leaf_node, options), - status, + status: 200, // TODO unless POST/PUT/PATCH/DELETE thinks otherwise error: null, branch: branch.filter(Boolean), fetched }); - } catch (err) { - const error = coalesce_to_error(err); - + } catch (error) { + // if we end up here, it means the data loaded successfull + // but the page failed to render options.handle_error(error, event); return await respond_with_error({ From fbd3512599eab0897d2eaf3d337a0785d4717e0c Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 5 Aug 2022 13:46:05 -0400 Subject: [PATCH 078/110] construct redirect URL properly --- packages/kit/src/runtime/server/page/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 481084e73eb8..a9f6d8b1862a 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -182,7 +182,7 @@ export async function render_page(event, route, options, state, resolve_opts) { }); } catch (error) { if (/** @type {Redirect} */ (error).__is_redirect) { - return Response.redirect(error.location, error.status); + return Response.redirect(new URL(error.location, event.url), error.status); } if (!error.__is_http_error) { @@ -315,7 +315,7 @@ async function handle_json_request(event, options, mod) { return new Response(undefined, { status: 204 }); } catch (error) { if (error?.__is_redirect) { - return Response.redirect(error.location, error.status); + return Response.redirect(new URL(error.location, event.url), error.status); } if (error?.__is_http_error) { From d17d3581a265719820e70fbbd8ca1a0df6793dd1 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 5 Aug 2022 14:53:59 -0400 Subject: [PATCH 079/110] fix redirects --- packages/kit/src/runtime/server/page/index.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index a9f6d8b1862a..874dbd0bbe02 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -182,7 +182,7 @@ export async function render_page(event, route, options, state, resolve_opts) { }); } catch (error) { if (/** @type {Redirect} */ (error).__is_redirect) { - return Response.redirect(new URL(error.location, event.url), error.status); + return redirect_response(error.status, error.location); } if (!error.__is_http_error) { @@ -315,7 +315,7 @@ async function handle_json_request(event, options, mod) { return new Response(undefined, { status: 204 }); } catch (error) { if (error?.__is_redirect) { - return Response.redirect(new URL(error.location, event.url), error.status); + return redirect_response(error.status, error.location); } if (error?.__is_http_error) { @@ -351,3 +351,14 @@ async function unwrap_promises(object) { return unwrapped; } + +/** + * @param {number} status + * @param {string} location + */ +function redirect_response(status, location) { + return new Response(undefined, { + status, + headers: { location } + }); +} From 27a4523e41260fb16328de0405db70f9c2302695 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 5 Aug 2022 15:34:44 -0400 Subject: [PATCH 080/110] handle POST requests etc --- packages/kit/src/runtime/server/page/index.js | 72 ++++++++++++++++--- packages/kit/src/runtime/server/utils.js | 23 +++--- 2 files changed, 74 insertions(+), 21 deletions(-) diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 874dbd0bbe02..1d2d5e5964aa 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -1,11 +1,11 @@ import { negotiate } from '../../../utils/http.js'; import { render_response } from './render.js'; import { respond_with_error } from './respond_with_error.js'; -import { coalesce_to_error } from '../../../utils/error.js'; -import { method_not_allowed, clone_error } from '../utils.js'; +import { method_not_allowed, clone_error, allowed_methods } from '../utils.js'; import { create_fetch } from './fetch.js'; import { LoadURL, PrerenderingURL } from '../../../utils/url.js'; import { Redirect } from '../../../index/private.js'; +import { error } from '../../../index/index.js'; /** * @typedef {import('./types.js').Loaded} Loaded @@ -53,10 +53,48 @@ export async function render_page(event, route, options, state, resolve_opts) { options.manifest._.nodes[route.page]() ]); - // TODO for non-GET requests, first call handler in +page.server.js - // (this also determines status code) const leaf_node = /** @type {import('types').SSRNode} */ (nodes.at(-1)); + let status = 200; + + /** @type {HttpError | Error} */ + let mutation_error; + + /** @type {Record} */ + let mutation_validation_errors; + + if (leaf_node.server && event.request.method !== 'GET' && event.request.method !== 'HEAD') { + // for non-GET requests, first call handler in +page.server.js + // (this also determines status code) + try { + const handler = leaf_node.server[event.request.method]; + if (handler) { + const result = await handler.call(null, event); + + if (result?.errors) { + mutation_validation_errors = result.errors; + status = result.status ?? 400; + } + + if (event.request.method === 'POST' && result?.location) { + return redirect_response(status, result.location); + } + } else { + event.setHeaders({ + allow: allowed_methods(leaf_node.server).join(', ') + }); + + mutation_error = error(405, 'Method not allowed'); + } + } catch (e) { + if (e.__is_redirect) { + return redirect_response(e.status, e.location); + } + + mutation_error = e; + } + } + if (!resolve_opts.ssr) { return await render_response({ branch: [], @@ -96,11 +134,21 @@ export async function render_page(event, route, options, state, resolve_opts) { let branch = []; /** @type {Error | null} */ - let error = null; + let load_error = null; /** @type {Array>} */ const server_promises = nodes.map((node, i) => { - if (error) throw error; // if an error happens immediately, don't bother with the rest of the nodes + if (load_error) { + // if an error happens immediately, don't bother with the rest of the nodes + throw load_error; + } + + if (node === leaf_node && mutation_error) { + // we wait until here to throw the error so that we can use + // any nested +error.svelte components that were defined + throw mutation_error; + } + return Promise.resolve().then(async () => { try { const server_data = node?.server?.GET?.call(null, { @@ -116,15 +164,15 @@ export async function render_page(event, route, options, state, resolve_opts) { return server_data ? unwrap_promises(server_data) : null; } catch (e) { - error = /** @type {Error} */ (e); - throw error; + load_error = /** @type {Error} */ (e); + throw load_error; } }); }); /** @type {Array | null>>} */ const load_promises = nodes.map((node, i) => { - if (error) throw error; + if (load_error) throw load_error; return Promise.resolve().then(async () => { try { const server_data = await server_promises[i]; @@ -160,8 +208,8 @@ export async function render_page(event, route, options, state, resolve_opts) { return server_data; } catch (e) { - error = /** @type {Error} */ (e); - throw error; + load_error = /** @type {Error} */ (e); + throw load_error; } }); }); @@ -230,6 +278,8 @@ export async function render_page(event, route, options, state, resolve_opts) { } } + // TODO use mutation_validation_errors + return await render_response({ event, options, diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index d7180e702ae6..5d2ef2a7550c 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -74,12 +74,22 @@ export const GENERIC_ERROR = { }; /** - * * @param {Record} mod * @param {import('types').HttpMethod} method - * @returns */ export function method_not_allowed(mod, method) { + return new Response(`${method} method not allowed`, { + status: 405, + headers: { + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 + // "The server must generate an Allow header field in a 405 status code response" + allow: allowed_methods(mod).join(', ') + } + }); +} + +/** @param {Record} mod */ +export function allowed_methods(mod) { const allowed = []; for (const method in ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']) { @@ -88,12 +98,5 @@ export function method_not_allowed(mod, method) { if (mod.GET || mod.HEAD) allowed.push('HEAD'); - return new Response(`${method} method not allowed`, { - status: 405, - headers: { - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405 - // "The server must generate an Allow header field in a 405 status code response" - allow: allowed.join(', ') - } - }); + return allowed; } From 55e1e584d8e86eef00ccb19e4ffa152d3c8e0e6b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 5 Aug 2022 16:08:29 -0400 Subject: [PATCH 081/110] oof this took an embarrassingly long time to find --- packages/kit/src/runtime/server/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index 6b3acb0b1905..631c8cef7364 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -261,7 +261,7 @@ export async function respond(request, options, state) { for (const key in headers) { const value = headers[key]; if (key === 'set-cookie') { - for (const cookie in Array.isArray(value) ? value : [value]) { + for (const cookie of Array.isArray(value) ? value : [value]) { response.headers.append(key, cookie); } } else { From ceb6b79cf1e2e76bf380e80dfe6cace60eb7fa94 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Fri, 5 Aug 2022 16:09:09 -0400 Subject: [PATCH 082/110] add types --- .../apps/basics/src/routes/shadowed/error-post/+page.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte b/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte index 040611ed374d..e49667f784c8 100644 --- a/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte +++ b/packages/kit/test/apps/basics/src/routes/shadowed/error-post/+page.svelte @@ -1,7 +1,10 @@ From ac5b00bd477b35f1c90cb31418b41a8ef0502589 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 6 Aug 2022 08:51:48 -0400 Subject: [PATCH 083/110] update/remove some tests as appropriate --- .../load-error-malformed-server/+page.js | 7 ---- .../load-error-malformed-server/+page.svelte | 0 .../routes/errors/load-error-server/+page.js | 2 +- .../load-status-without-error-server/+page.js | 6 ---- .../+page.svelte | 0 packages/kit/test/apps/basics/test/test.js | 35 ++++++------------- 6 files changed, 12 insertions(+), 38 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js deleted file mode 100644 index 75c4c4c3be17..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.js +++ /dev/null @@ -1,7 +0,0 @@ -import { error } from '@sveltejs/kit'; - -/** @type {import('@sveltejs/kit').Load} */ -export async function load() { - // @ts-expect-error - given value expected to throw - throw error(555, {}); -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-error-malformed-server/+page.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js index c6548d0f7d73..f27960c3bdec 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/load-error-server/+page.js @@ -2,5 +2,5 @@ import { error } from '@sveltejs/kit'; /** @type {import('@sveltejs/kit').Load} */ export async function load() { - throw error(555, new Error('Not found')); + throw error(555, 'Not found'); } diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js deleted file mode 100644 index c149968ee4df..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.js +++ /dev/null @@ -1,6 +0,0 @@ -import { error } from '@sveltejs/kit'; - -/** @type {import('@sveltejs/kit').Load} */ -export async function load() { - throw error(401, undefined); -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/load-status-without-error-server/+page.svelte deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 83a4c5b780b3..c62e074390bf 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -429,16 +429,6 @@ test.describe('Errors', () => { expect(/** @type {Response} */ (response).status()).toBe(555); }); - test('server-side error from load() is malformed', async ({ page }) => { - await page.goto('/errors/load-error-malformed-server'); - - const body = await page.textContent('body'); - - expect(body).toMatch( - 'Error: "error" property returned from load() must be a string or instance of Error, received type "object"' - ); - }); - test('error in endpoint', async ({ page, read_errors }) => { const res = await page.goto('/errors/endpoint'); @@ -451,7 +441,9 @@ test.describe('Errors', () => { } expect(res && res.status()).toBe(500); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: ""'); + expect(await page.textContent('#message')).toBe( + 'This is your custom error page saying: "Error"' + ); const contents = await page.textContent('#stack'); const location = /endpoint\.svelte:12:9|endpoint\.svelte:12:15/; // TODO: Remove second location with Vite 2.9 @@ -469,7 +461,9 @@ test.describe('Errors', () => { expect(read_errors('/errors/endpoint-not-ok.json')).toBeUndefined(); expect(res && res.status()).toBe(555); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: ""'); + expect(await page.textContent('#message')).toBe( + 'This is your custom error page saying: "Error"' + ); const contents = await page.textContent('#stack'); const location = /endpoint-not-ok\.svelte:12:9|endpoint-not-ok\.svelte:12:15/; // TODO: Remove second location with Vite 2.9 @@ -482,6 +476,8 @@ test.describe('Errors', () => { }); test('error in shadow endpoint', async ({ page, read_errors }) => { + const location = '+page.server.js:3:8'; + const res = await page.goto('/errors/endpoint-shadow'); // should include stack trace @@ -489,7 +485,7 @@ test.describe('Errors', () => { expect(lines[0]).toMatch('nope'); if (process.env.DEV) { - expect(lines[1]).toMatch('endpoint-shadow.js:3:8'); + expect(lines[1]).toMatch(location); } expect(res && res.status()).toBe(500); @@ -498,7 +494,6 @@ test.describe('Errors', () => { ); const contents = await page.textContent('#stack'); - const location = 'endpoint-shadow.js:3:8'; if (process.env.DEV) { expect(contents).toMatch(location); @@ -514,18 +509,10 @@ test.describe('Errors', () => { expect(res && res.status()).toBe(555); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: "Failed to load data"' + 'This is your custom error page saying: "Error: 555"' ); }); - test('server-side 4xx status without error from load()', async ({ page }) => { - const response = await page.goto('/errors/load-status-without-error-server'); - - expect(await page.textContent('footer')).toBe('Custom layout'); - expect(await page.textContent('#message')).toBe('This is your custom error page saying: "401"'); - expect(/** @type {Response} */ (response).status()).toBe(401); - }); - test('error thrown in handle results in a rendered error page', async ({ page }) => { await page.goto('/errors/error-in-handle'); @@ -572,7 +559,7 @@ test.describe('Errors', () => { if (process.env.DEV) { const lines = stack.split('\n'); - expect(lines[1]).toContain('get-implicit.js:4:8'); + expect(lines[1]).toContain('+page.server.js:4:8'); } const error = read_errors('/errors/page-endpoint/get-implicit'); From 93b1e34558ec199317e2f57d814e09dbbe68e5cc Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 7 Aug 2022 12:18:48 -0400 Subject: [PATCH 084/110] implement $page.data on the server --- .../kit/src/runtime/server/page/render.js | 3 +- .../src/routes/store/data/+error.svelte | 6 +++ .../routes/store/{stuff => data}/+layout.js | 0 .../src/routes/store/data/+layout.svelte | 13 +++++ .../src/routes/store/data/[item]/+page.js | 17 +++++++ .../store/{stuff => data}/[item]/+page.svelte | 0 .../routes/store/{stuff => data}/foo/+page.js | 0 .../store/{stuff => data}/foo/+page.svelte | 0 .../src/routes/store/stuff/+error.svelte | 20 -------- .../src/routes/store/stuff/+layout.svelte | 9 ---- .../src/routes/store/stuff/[item]/+page.js | 24 ---------- packages/kit/test/apps/basics/test/test.js | 48 ++++++++++--------- packages/kit/types/index.d.ts | 1 + 13 files changed, 65 insertions(+), 76 deletions(-) create mode 100644 packages/kit/test/apps/basics/src/routes/store/data/+error.svelte rename packages/kit/test/apps/basics/src/routes/store/{stuff => data}/+layout.js (100%) create mode 100644 packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte create mode 100644 packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js rename packages/kit/test/apps/basics/src/routes/store/{stuff => data}/[item]/+page.svelte (100%) rename packages/kit/test/apps/basics/src/routes/store/{stuff => data}/foo/+page.js (100%) rename packages/kit/test/apps/basics/src/routes/store/{stuff => data}/foo/+page.svelte (100%) delete mode 100644 packages/kit/test/apps/basics/src/routes/store/stuff/+error.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/store/stuff/+layout.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js diff --git a/packages/kit/src/runtime/server/page/render.js b/packages/kit/src/runtime/server/page/render.js index 009bdcba1499..6410cc444f0e 100644 --- a/packages/kit/src/runtime/server/page/render.js +++ b/packages/kit/src/runtime/server/page/render.js @@ -105,7 +105,8 @@ export async function render_response({ params: event.params, routeId: event.routeId, status, - url: state.prerendering ? new PrerenderingURL(event.url) : event.url + url: state.prerendering ? new PrerenderingURL(event.url) : event.url, + data: branch.reduce((acc, { data }) => (Object.assign(acc, data), acc), {}) }, components: branch.map(({ node }) => node.component) }; diff --git a/packages/kit/test/apps/basics/src/routes/store/data/+error.svelte b/packages/kit/test/apps/basics/src/routes/store/data/+error.svelte new file mode 100644 index 000000000000..9f5c4cf0a40d --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/+error.svelte @@ -0,0 +1,6 @@ + + + diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/+layout.js b/packages/kit/test/apps/basics/src/routes/store/data/+layout.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/stuff/+layout.js rename to packages/kit/test/apps/basics/src/routes/store/data/+layout.js diff --git a/packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte b/packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte new file mode 100644 index 000000000000..1ad81a9ca762 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/+layout.svelte @@ -0,0 +1,13 @@ + + +
{JSON.stringify($page.data)}
+
{$page.error?.message}
+ + + + diff --git a/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js b/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js new file mode 100644 index 000000000000..f7777e8237e6 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.js @@ -0,0 +1,17 @@ +import { error } from '@sveltejs/kit'; + +/** @type {import('./$types').Load} */ +export function load({ params }) { + if (params.item === 'xxx') { + throw error(500, 'Params = xxx'); + } + + if (params.item === 'yyy') { + throw error(500, 'Params = yyy'); + } + + return { + page: params.item, + value: 456 + }; +} diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.svelte b/packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.svelte rename to packages/kit/test/apps/basics/src/routes/store/data/[item]/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.js b/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.js rename to packages/kit/test/apps/basics/src/routes/store/data/foo/+page.js diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.svelte b/packages/kit/test/apps/basics/src/routes/store/data/foo/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/store/stuff/foo/+page.svelte rename to packages/kit/test/apps/basics/src/routes/store/data/foo/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/+error.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/+error.svelte deleted file mode 100644 index 8266d8b126e7..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/+error.svelte +++ /dev/null @@ -1,20 +0,0 @@ - - - - - diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/+layout.svelte b/packages/kit/test/apps/basics/src/routes/store/stuff/+layout.svelte deleted file mode 100644 index 27ac806ded8a..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/+layout.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - -
{JSON.stringify($page.stuff)}
- - - - diff --git a/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js b/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js deleted file mode 100644 index 4b5ef327743c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/store/stuff/[item]/+page.js +++ /dev/null @@ -1,24 +0,0 @@ -import { error } from '@sveltejs/kit'; - -/** @type {import('@sveltejs/kit').Load} */ -export function load({ params }) { - if (params.item === 'xxx') { - throw new Error('Params = xxx'); - } else if (param.item === 'yyy') { - throw error(500, 'Params = yyy'); - } else { - return { - page: params.item, - value: 456 - }; - } - - // TODO check test; old code - // return { - // stuff: { - // page: params.item, - // value: 456 - // }, - // error: params.item === 'yyy' ? 'Params = yyy' : undefined - // }; -} diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index c62e074390bf..e5bb224e0065 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1096,48 +1096,52 @@ test.describe('$app/stores', () => { expect(oops).toBeUndefined(); }); - test('page store contains stuff', async ({ page, clicknav }) => { - await page.goto('/store/stuff/www'); + test('page store contains data', async ({ page, clicknav }) => { + await page.goto('/store/data/www'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 456, page: 'www' }) + const foo = { bar: 'Custom layout' }; + + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 456, page: 'www' }) ); - await clicknav('a[href="/store/stuff/zzz"]'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 456, page: 'zzz' }) + await clicknav('a[href="/store/data/zzz"]'); + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 456, page: 'zzz' }) ); - await clicknav('a[href="/store/stuff/xxx"]'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 789, error: 'Params = xxx' }) + await clicknav('a[href="/store/data/xxx"]'); + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 123 }) ); + expect(await page.textContent('#store-error')).toBe('Params = xxx'); - await clicknav('a[href="/store/stuff/yyy"]'); - expect(await page.textContent('#store-stuff')).toBe( - JSON.stringify({ name: 'SvelteKit', value: 789, error: 'Params = yyy' }) + await clicknav('a[href="/store/data/yyy"]'); + expect(await page.textContent('#store-data')).toBe( + JSON.stringify({ foo, name: 'SvelteKit', value: 123 }) ); + expect(await page.textContent('#store-error')).toBe('Params = yyy'); }); - test('should load stuff after reloading by goto', async ({ + test('should load data after reloading by goto', async ({ page, clicknav, javaScriptEnabled }) => { - await page.goto('/store/stuff/foo?reset=true'); + await page.goto('/store/data/foo?reset=true'); const stuff1 = JSON.stringify({ name: 'SvelteKit', value: 789, error: 'uh oh' }); const stuff2 = JSON.stringify({ name: 'SvelteKit', value: 123, foo: true }); - await page.goto('/store/stuff/www'); + await page.goto('/store/data/www'); - await clicknav('a[href="/store/stuff/foo"]'); - expect(await page.textContent('#store-stuff')).toBe(stuff1); + await clicknav('a[href="/store/data/foo"]'); + expect(await page.textContent('#store-data')).toBe(stuff1); await clicknav('#reload-button'); - expect(await page.textContent('#store-stuff')).toBe(javaScriptEnabled ? stuff2 : stuff1); + expect(await page.textContent('#store-data')).toBe(javaScriptEnabled ? stuff2 : stuff1); - await clicknav('a[href="/store/stuff/zzz"]'); - await clicknav('a[href="/store/stuff/foo"]'); - expect(await page.textContent('#store-stuff')).toBe(stuff2); + await clicknav('a[href="/store/data/zzz"]'); + await clicknav('a[href="/store/data/foo"]'); + expect(await page.textContent('#store-data')).toBe(stuff2); }); test('navigating store contains from and to', async ({ app, page, javaScriptEnabled }) => { diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index e9fbb96324c8..3f22e5c7a8cd 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -216,6 +216,7 @@ export interface Page = Record; } export interface ParamMatcher { From e3c545db707c796fe478daf54280dbf1c4fba0d3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 7 Aug 2022 12:33:34 -0400 Subject: [PATCH 085/110] fix test --- packages/kit/test/apps/basics/test/test.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index e5bb224e0065..c339d364e163 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1129,19 +1129,22 @@ test.describe('$app/stores', () => { javaScriptEnabled }) => { await page.goto('/store/data/foo?reset=true'); - const stuff1 = JSON.stringify({ name: 'SvelteKit', value: 789, error: 'uh oh' }); - const stuff2 = JSON.stringify({ name: 'SvelteKit', value: 123, foo: true }); + const stuff1 = { foo: { bar: 'Custom layout' }, name: 'SvelteKit', value: 123 }; + const stuff2 = { ...stuff1, foo: true }; + const stuff3 = { ...stuff2, number: 2 }; await page.goto('/store/data/www'); await clicknav('a[href="/store/data/foo"]'); - expect(await page.textContent('#store-data')).toBe(stuff1); + expect(JSON.parse(await page.textContent('#store-data'))).toEqual(stuff1); await clicknav('#reload-button'); - expect(await page.textContent('#store-data')).toBe(javaScriptEnabled ? stuff2 : stuff1); + expect(JSON.parse(await page.textContent('#store-data'))).toEqual( + javaScriptEnabled ? stuff2 : stuff1 + ); await clicknav('a[href="/store/data/zzz"]'); await clicknav('a[href="/store/data/foo"]'); - expect(await page.textContent('#store-data')).toBe(stuff2); + expect(JSON.parse(await page.textContent('#store-data'))).toEqual(stuff3); }); test('navigating store contains from and to', async ({ app, page, javaScriptEnabled }) => { From 54d2b66bb0cf963f0161408a13fdb2f826706465 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 7 Aug 2022 12:49:03 -0400 Subject: [PATCH 086/110] handle invalid redirect statuses --- packages/kit/src/index/index.js | 4 ++++ packages/kit/test/apps/basics/test/test.js | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/index/index.js b/packages/kit/src/index/index.js index c7a885ae8c63..eeaff257ee9a 100644 --- a/packages/kit/src/index/index.js +++ b/packages/kit/src/index/index.js @@ -13,5 +13,9 @@ export function error(status, message) { * @param {string} location */ export function redirect(status, location) { + if (isNaN(status) || status < 300 || status > 399) { + throw new Error('Invalid status code'); + } + return new Redirect(status, location); } diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index c339d364e163..24f0113acb14 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1280,15 +1280,13 @@ test.describe('Redirects', () => { expect(page.url()).toBe(`${baseURL}/redirect/missing-status/a`); expect(await page.textContent('h1')).toBe('500'); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: ""redirect" property returned from load() must be accompanied by a 3xx status code"' + 'This is your custom error page saying: "Invalid status code"' ); if (!javaScriptEnabled) { // handleError is not invoked for client-side navigation const lines = read_errors('/redirect/missing-status/a').split('\n'); - expect(lines[0]).toBe( - 'Error: "redirect" property returned from load() must be accompanied by a 3xx status code' - ); + expect(lines[0]).toBe('Error: Invalid status code'); } }); @@ -1300,7 +1298,7 @@ test.describe('Redirects', () => { expect(page.url()).toBe(`${baseURL}/redirect/missing-status/b`); expect(await page.textContent('h1')).toBe('500'); expect(await page.textContent('#message')).toBe( - 'This is your custom error page saying: ""redirect" property returned from load() must be accompanied by a 3xx status code"' + 'This is your custom error page saying: "Invalid status code"' ); }); From 82393e5ab52fd1d139c7de0ffe1b48243c0a9d39 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 7 Aug 2022 13:00:13 -0400 Subject: [PATCH 087/110] update test, fix off-by-one error --- packages/kit/src/runtime/server/page/index.js | 4 ++-- .../basics/src/routes/load/{stuff => parent}/+layout.js | 0 .../src/routes/load/{stuff => parent}/+layout.svelte | 0 .../src/routes/load/{stuff => parent}/[x]/+layout.js | 0 .../src/routes/load/{stuff => parent}/[x]/+layout.svelte | 0 .../src/routes/load/{stuff => parent}/[x]/[y]/+layout.js | 0 .../routes/load/{stuff => parent}/[x]/[y]/+layout.svelte | 0 .../routes/load/{stuff => parent}/[x]/[y]/[z]/+page.js | 0 .../src/routes/load/parent/[x]/[y]/[z]/+page.svelte | 6 ++++++ .../basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte | 7 ------- packages/kit/test/apps/basics/test/test.js | 8 +++++--- 11 files changed, 13 insertions(+), 12 deletions(-) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/+layout.js (100%) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/+layout.svelte (100%) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/[x]/+layout.js (100%) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/[x]/+layout.svelte (100%) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/[x]/[y]/+layout.js (100%) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/[x]/[y]/+layout.svelte (100%) rename packages/kit/test/apps/basics/src/routes/load/{stuff => parent}/[x]/[y]/[z]/+page.js (100%) create mode 100644 packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte delete mode 100644 packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index 1d2d5e5964aa..c5e412b4f635 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -155,7 +155,7 @@ export async function render_page(event, route, options, state, resolve_opts) { ...event, parent: async () => { const data = {}; - for (let j = 0; j < i - 1; j += 1) { + for (let j = 0; j < i; j += 1) { Object.assign(data, await server_promises[j]); } return data; @@ -196,7 +196,7 @@ export async function render_page(event, route, options, state, resolve_opts) { depends: () => {}, parent: async () => { const data = {}; - for (let j = 0; j < i - 1; j += 1) { + for (let j = 0; j < i; j += 1) { Object.assign(data, await load_promises[j]); } return data; diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/+layout.js b/packages/kit/test/apps/basics/src/routes/load/parent/+layout.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/+layout.js rename to packages/kit/test/apps/basics/src/routes/load/parent/+layout.js diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/+layout.svelte rename to packages/kit/test/apps/basics/src/routes/load/parent/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/+layout.js b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/[x]/+layout.js rename to packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.js diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/[x]/+layout.svelte rename to packages/kit/test/apps/basics/src/routes/load/parent/[x]/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/+layout.js b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/+layout.js rename to packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.js diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/+layout.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/+layout.svelte rename to packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/+layout.svelte diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.js b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.js rename to packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.js diff --git a/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte new file mode 100644 index 000000000000..0b85507c19d8 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/parent/[x]/[y]/[z]/+page.svelte @@ -0,0 +1,6 @@ + + +

message: {$page.data.message}

+
{JSON.stringify($page.data)}
diff --git a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte deleted file mode 100644 index 22583491d83f..000000000000 --- a/packages/kit/test/apps/basics/src/routes/load/stuff/[x]/[y]/[z]/+page.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - -

message: {data.message}

-
{JSON.stringify(data)}
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 24f0113acb14..a3aafcbead51 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -719,11 +719,13 @@ test.describe('Load', () => { expect(await page.textContent('h1')).toBe('static file'); }); - test('stuff is inherited', async ({ page, javaScriptEnabled, app }) => { - await page.goto('/load/stuff/a/b/c'); + test('data is inherited', async ({ page, javaScriptEnabled, app }) => { + await page.goto('/load/parent/a/b/c'); expect(await page.textContent('h1')).toBe('message: original + new'); expect(await page.textContent('pre')).toBe( JSON.stringify({ + foo: { bar: 'Custom layout' }, + message: 'original + new', x: 'a', y: 'b', z: 'c' @@ -731,7 +733,7 @@ test.describe('Load', () => { ); if (javaScriptEnabled) { - await app.goto('/load/stuff/d/e/f'); + await app.goto('/load/parent/d/e/f'); expect(await page.textContent('h1')).toBe('message: original + new'); expect(await page.textContent('pre')).toBe( From 18df10e956adf62967a025e8fd4390e9bf3c8bbf Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sun, 7 Aug 2022 14:13:56 -0400 Subject: [PATCH 088/110] fix a bunch more tests --- packages/kit/src/runtime/server/page/index.js | 14 ++++---- .../errors/endpoint-not-ok.json/+server.js | 4 --- .../routes/errors/endpoint-not-ok/+page.js | 11 ------- .../errors/endpoint-not-ok/+page.svelte | 1 - .../src/routes/errors/endpoint/+page.js | 4 +-- .../nested-layout/foo/bar/+error.svelte | 23 ++----------- packages/kit/test/apps/basics/test/test.js | 32 ++++--------------- 7 files changed, 17 insertions(+), 72 deletions(-) delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json/+server.js delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js delete mode 100644 packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.svelte diff --git a/packages/kit/src/runtime/server/page/index.js b/packages/kit/src/runtime/server/page/index.js index c5e412b4f635..435f0ebb354e 100644 --- a/packages/kit/src/runtime/server/page/index.js +++ b/packages/kit/src/runtime/server/page/index.js @@ -113,21 +113,21 @@ export async function render_page(event, route, options, state, resolve_opts) { }); } - if (state.prerendering) { + const should_prerender = leaf_node.module?.prerender ?? options.prerender.default; + if (should_prerender) { + const mod = leaf_node.server; + if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { + throw new Error('Cannot prerender pages that have endpoints with mutative methods'); + } + } else if (state.prerendering) { // if the page isn't marked as prerenderable (or is explicitly // marked NOT prerenderable, if `prerender.default` is `true`), // then bail out at this point - const should_prerender = leaf_node.module.prerender ?? options.prerender.default; if (!should_prerender) { return new Response(undefined, { status: 204 }); } - - const mod = leaf_node.server; - if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) { - throw new Error('Cannot prerender pages that have endpoints with mutative methods'); - } } /** @type {Array} */ diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json/+server.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json/+server.js deleted file mode 100644 index e0c497e7fbfb..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok.json/+server.js +++ /dev/null @@ -1,4 +0,0 @@ -/** @type {import('@sveltejs/kit').RequestHandler} */ -export function GET() { - return new Response(undefined, { status: 555 }); -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js deleted file mode 100644 index 1b676e6c1946..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.js +++ /dev/null @@ -1,11 +0,0 @@ -import { error } from '@sveltejs/kit'; - -/** @type {import('@sveltejs/kit').Load} */ -export async function load({ fetch }) { - const res = await fetch('/errors/endpoint-not-ok.json'); - if (res.ok) { - return await res.json(); - } else { - throw error(res.status, new Error(res.statusText)); - } -} diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.svelte b/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.svelte deleted file mode 100644 index 5480d57ef12c..000000000000 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint-not-ok/+page.svelte +++ /dev/null @@ -1 +0,0 @@ -

this text should not appear

diff --git a/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js index 4f92bd4ea33d..1b9b8ea2ba2d 100644 --- a/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js +++ b/packages/kit/test/apps/basics/src/routes/errors/endpoint/+page.js @@ -1,11 +1,9 @@ -import { error } from '@sveltejs/kit'; - /** @type {import('@sveltejs/kit').Load} */ export async function load({ fetch }) { const res = await fetch('/errors/endpoint.json'); if (res.ok) { return await res.json(); } else { - throw error(res.status, new Error(res.statusText)); + throw new Error(String(res.status)); } } diff --git a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte index e745736ec46f..5301c04c4566 100644 --- a/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte +++ b/packages/kit/test/apps/basics/src/routes/nested-layout/foo/bar/+error.svelte @@ -1,27 +1,10 @@ - -

Nested error page

-

status: {data.status}

-

error.message: {data.error && data.error.message}

-

answer: {data.answer}

+

status: {$page.status}

+

error.message: {$page.error.message}