diff --git a/.changeset/dirty-radios-smile.md b/.changeset/dirty-radios-smile.md new file mode 100644 index 000000000000..027fa152ce04 --- /dev/null +++ b/.changeset/dirty-radios-smile.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +fix: include universal load assets as server assets diff --git a/packages/kit/src/core/generate_manifest/find_server_assets.js b/packages/kit/src/core/generate_manifest/find_server_assets.js index 3a562f4b53a0..b7232e63e0b8 100644 --- a/packages/kit/src/core/generate_manifest/find_server_assets.js +++ b/packages/kit/src/core/generate_manifest/find_server_assets.js @@ -41,6 +41,7 @@ export function find_server_assets(build_data, routes) { for (const n of used_nodes) { const node = build_data.manifest_data.nodes[n]; + if (node?.universal) add_assets(node.universal); if (node?.server) add_assets(node.server); } diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts index f25cc225e194..7248f12a4a35 100644 --- a/packages/kit/src/exports/public.d.ts +++ b/packages/kit/src/exports/public.d.ts @@ -1177,7 +1177,7 @@ export interface RequestEvent< * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle) * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request. * - * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies) + * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies). */ fetch: typeof fetch; /** @@ -1189,7 +1189,7 @@ export interface RequestEvent< */ locals: App.Locals; /** - * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object + * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object. */ params: Params; /** @@ -1197,15 +1197,15 @@ export interface RequestEvent< */ platform: Readonly | undefined; /** - * The original request object + * The original request object. */ request: Request; /** - * Info about the current route + * Info about the current route. */ route: { /** - * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]` + * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`. */ id: RouteId; }; @@ -1302,15 +1302,16 @@ export class Server { } export interface ServerInitOptions { - /** A map of environment variables */ + /** A map of environment variables. */ env: Record; - /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */ + /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work. */ read?: (file: string) => ReadableStream; } export interface SSRManifest { appDir: string; appPath: string; + /** Static files from `kit.config.files.assets` and the service worker (if any). */ assets: Set; mimeTypes: Record; @@ -1321,7 +1322,7 @@ export interface SSRManifest { routes: SSRRoute[]; prerendered_routes: Set; matchers: () => Promise>; - /** A `[file]: size` map of all assets imported by server code */ + /** A `[file]: size` map of all assets imported by server code. */ server_assets: Record; }; } @@ -1452,7 +1453,7 @@ export interface HttpError { } /** - * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function + * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function. */ export interface Redirect { /** The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages), in the range 300-308. */ diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index f67e6e7c3606..656f52060500 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -68,24 +68,24 @@ export interface BuildData { out_dir: string; service_worker: string | null; client: { - /** Path to the client entry point */ + /** Path to the client entry point. */ start: string; - /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'` */ + /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'`. */ app?: string; - /** JS files that the client entry point relies on */ + /** JS files that the client entry point relies on. */ imports: string[]; /** * JS files that represent the entry points of the layouts/pages. * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file). * Only set in case of `router.resolution === 'server'`. */ - nodes?: (string | undefined)[]; + nodes?: Array; /** * CSS files referenced in the entry points of the layouts/pages. * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file) or if has no CSS. * Only set in case of `router.resolution === 'server'`. */ - css?: (string[] | undefined)[]; + css?: Array; /** * Contains the client route manifest in a form suitable for the server which is used for server side route resolution. * Notably, it contains all routes, regardless of whether they are prerendered or not (those are missing in the optimized server route manifest). @@ -95,7 +95,7 @@ export interface BuildData { stylesheets: string[]; fonts: string[]; uses_env_dynamic_public: boolean; - /** Only set in case of `bundleStrategy === 'inline'` */ + /** Only set in case of `bundleStrategy === 'inline'`. */ inline?: { script: string; style: string | undefined; @@ -172,7 +172,7 @@ export class InternalServer extends Server { options: RequestOptions & { prerendering?: PrerenderOptions; read: (file: string) => Buffer; - /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated */ + /** A hook called before `handle` during dev, so that `AsyncLocalStorage` can be populated. */ before_handle?: (event: RequestEvent, config: any, prerender: PrerenderOption) => void; emulator?: Emulator; } @@ -180,6 +180,7 @@ export class InternalServer extends Server { } export interface ManifestData { + /** Static files from `kit.config.files.assets`. */ assets: Asset[]; hooks: { client: string | null; @@ -193,15 +194,15 @@ export interface ManifestData { export interface PageNode { depth: number; - /** The +page/layout.svelte */ + /** The `+page/layout.svelte`. */ component?: string; // TODO supply default component if it's missing (bit of an edge case) - /** The +page/layout.js/.ts */ + /** The `+page/layout.js/.ts`. */ universal?: string; - /** The +page/layout.server.js/ts */ + /** The `+page/layout.server.js/ts`. */ server?: string; parent_id?: string; parent?: PageNode; - /** Filled with the pages that reference this layout (if this is a layout) */ + /** Filled with the pages that reference this layout (if this is a layout). */ child_pages?: PageNode[]; } @@ -219,6 +220,7 @@ export interface PrerenderOptions { export type RecursiveRequired = { // Recursive implementation of TypeScript's Required utility type. // Will recursively continue until it reaches a primitive or Function + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type [K in keyof T]-?: Extract extends never // If it does not have a Function type ? RecursiveRequired // recursively continue through. : T[K]; // Use the exact type for everything else @@ -305,20 +307,20 @@ export interface ServerDataChunkNode { /** * Signals that the server `load` function was not run, and the - * client should use what it has in memory + * client should use what it has in memory. */ export interface ServerDataSkippedNode { type: 'skip'; } /** - * Signals that the server `load` function failed + * Signals that the server `load` function failed. */ export interface ServerErrorNode { type: 'error'; error: App.Error; /** - * Only set for HttpErrors + * Only set for HttpErrors. */ status?: number; } @@ -338,7 +340,7 @@ export interface ServerMetadataRoute { export interface ServerMetadata { nodes: Array<{ - /** Also `true` when using `trailingSlash`, because we need to do a server request in that case to get its value */ + /** Also `true` when using `trailingSlash`, because we need to do a server request in that case to get its value. */ has_server_load: boolean; }>; routes: Map; @@ -364,7 +366,7 @@ export type SSRComponentLoader = () => Promise; export interface SSRNode { component: SSRComponentLoader; - /** index into the `nodes` array in the generated `client/app.js` */ + /** index into the `nodes` array in the generated `client/app.js`. */ index: number; /** external JS files that are loaded on the client. `imports[0]` is the entry point (e.g. `client/nodes/0.js`) */ imports: string[]; @@ -372,7 +374,7 @@ export interface SSRNode { stylesheets: string[]; /** external font files that are loaded on the client */ fonts: string[]; - /** inlined styles */ + /** inlined styles. */ inline_styles?(): MaybePromise>; universal: { @@ -465,18 +467,18 @@ export interface SSRState { fallback?: string; getClientAddress(): string; /** - * True if we're currently attempting to render an error page + * True if we're currently attempting to render an error page. */ error: boolean; /** - * Allows us to prevent `event.fetch` from making infinitely looping internal requests + * Allows us to prevent `event.fetch` from making infinitely looping internal requests. */ depth: number; platform?: any; prerendering?: PrerenderOptions; /** * When fetching data from a +server.js endpoint in `load`, the page's - * prerender option is inherited by the endpoint, unless overridden + * prerender option is inherited by the endpoint, unless overridden. */ prerender_default?: PrerenderOption; read?: (file: string) => Buffer; diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-asset/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-asset/+page.js new file mode 100644 index 000000000000..1e874734d24a --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-asset/+page.js @@ -0,0 +1,9 @@ +// @ts-expect-error we need both queries to prevent Vite from inlining the asset as base64 string on build +// see https://github.com/vitejs/vite/issues/19562 +import asset from './example.json?url&no-inline'; + +export async function load({ fetch }) { + const res = await fetch(asset); + const data = await res.json(); + return data; +} diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-asset/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-asset/+page.svelte new file mode 100644 index 000000000000..fddeb0d2e5b9 --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-asset/+page.svelte @@ -0,0 +1,5 @@ + + +

{data.a}

diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-asset/example.json b/packages/kit/test/apps/basics/src/routes/load/fetch-asset/example.json new file mode 100644 index 000000000000..aa53c37972ab --- /dev/null +++ b/packages/kit/test/apps/basics/src/routes/load/fetch-asset/example.json @@ -0,0 +1,3 @@ +{ + "a": "1" +} diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index e56b8231fb46..f05101c4633d 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -477,6 +477,11 @@ test.describe('Load', () => { expect(await response.text()).toContain('status: 404'); }); + test('fetch reads universal load assets on the server', async ({ page }) => { + await page.goto('/load/fetch-asset'); + await expect(page.locator('p')).toHaveText('1'); + }); + test('includes origin header on non-GET internal request', async ({ page, baseURL }) => { await page.goto('/load/fetch-origin-internal'); expect(await page.textContent('h1')).toBe(`origin: ${new URL(baseURL).origin}`); diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index f7ca3bce33f9..2534ab89ecad 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -1159,7 +1159,7 @@ declare module '@sveltejs/kit' { * - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://svelte.dev/docs/kit/hooks#Server-hooks-handle) * - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request. * - * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies) + * You can learn more about making credentialed requests with cookies [here](https://svelte.dev/docs/kit/load#Cookies). */ fetch: typeof fetch; /** @@ -1171,7 +1171,7 @@ declare module '@sveltejs/kit' { */ locals: App.Locals; /** - * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object + * The parameters of the current route - e.g. for a route like `/blog/[slug]`, a `{ slug: string }` object. */ params: Params; /** @@ -1179,15 +1179,15 @@ declare module '@sveltejs/kit' { */ platform: Readonly | undefined; /** - * The original request object + * The original request object. */ request: Request; /** - * Info about the current route + * Info about the current route. */ route: { /** - * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]` + * The ID of the current route - e.g. for `src/routes/blog/[slug]`, it would be `/blog/[slug]`. */ id: RouteId; }; @@ -1284,15 +1284,16 @@ declare module '@sveltejs/kit' { } export interface ServerInitOptions { - /** A map of environment variables */ + /** A map of environment variables. */ env: Record; - /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work */ + /** A function that turns an asset filename into a `ReadableStream`. Required for the `read` export from `$app/server` to work. */ read?: (file: string) => ReadableStream; } export interface SSRManifest { appDir: string; appPath: string; + /** Static files from `kit.config.files.assets` and the service worker (if any). */ assets: Set; mimeTypes: Record; @@ -1303,7 +1304,7 @@ declare module '@sveltejs/kit' { routes: SSRRoute[]; prerendered_routes: Set; matchers: () => Promise>; - /** A `[file]: size` map of all assets imported by server code */ + /** A `[file]: size` map of all assets imported by server code. */ server_assets: Record; }; } @@ -1434,7 +1435,7 @@ declare module '@sveltejs/kit' { } /** - * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function + * The object returned by the [`redirect`](https://svelte.dev/docs/kit/@sveltejs-kit#redirect) function. */ export interface Redirect { /** The [HTTP status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages), in the range 300-308. */ @@ -1715,24 +1716,24 @@ declare module '@sveltejs/kit' { out_dir: string; service_worker: string | null; client: { - /** Path to the client entry point */ + /** Path to the client entry point. */ start: string; - /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'` */ + /** Path to the generated `app.js` file that contains the client manifest. Only set in case of `bundleStrategy === 'split'`. */ app?: string; - /** JS files that the client entry point relies on */ + /** JS files that the client entry point relies on. */ imports: string[]; /** * JS files that represent the entry points of the layouts/pages. * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file). * Only set in case of `router.resolution === 'server'`. */ - nodes?: (string | undefined)[]; + nodes?: Array; /** * CSS files referenced in the entry points of the layouts/pages. * An entry is undefined if the layout/page has no component or universal file (i.e. only has a `.server.js` file) or if has no CSS. * Only set in case of `router.resolution === 'server'`. */ - css?: (string[] | undefined)[]; + css?: Array; /** * Contains the client route manifest in a form suitable for the server which is used for server side route resolution. * Notably, it contains all routes, regardless of whether they are prerendered or not (those are missing in the optimized server route manifest). @@ -1742,7 +1743,7 @@ declare module '@sveltejs/kit' { stylesheets: string[]; fonts: string[]; uses_env_dynamic_public: boolean; - /** Only set in case of `bundleStrategy === 'inline'` */ + /** Only set in case of `bundleStrategy === 'inline'`. */ inline?: { script: string; style: string | undefined; @@ -1752,6 +1753,7 @@ declare module '@sveltejs/kit' { } interface ManifestData { + /** Static files from `kit.config.files.assets`. */ assets: Asset[]; hooks: { client: string | null; @@ -1765,21 +1767,22 @@ declare module '@sveltejs/kit' { interface PageNode { depth: number; - /** The +page/layout.svelte */ + /** The `+page/layout.svelte`. */ component?: string; // TODO supply default component if it's missing (bit of an edge case) - /** The +page/layout.js/.ts */ + /** The `+page/layout.js/.ts`. */ universal?: string; - /** The +page/layout.server.js/ts */ + /** The `+page/layout.server.js/ts`. */ server?: string; parent_id?: string; parent?: PageNode; - /** Filled with the pages that reference this layout (if this is a layout) */ + /** Filled with the pages that reference this layout (if this is a layout). */ child_pages?: PageNode[]; } type RecursiveRequired = { // Recursive implementation of TypeScript's Required utility type. // Will recursively continue until it reaches a primitive or Function + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type [K in keyof T]-?: Extract extends never // If it does not have a Function type ? RecursiveRequired // recursively continue through. : T[K]; // Use the exact type for everything else @@ -1841,7 +1844,7 @@ declare module '@sveltejs/kit' { interface SSRNode { component: SSRComponentLoader; - /** index into the `nodes` array in the generated `client/app.js` */ + /** index into the `nodes` array in the generated `client/app.js`. */ index: number; /** external JS files that are loaded on the client. `imports[0]` is the entry point (e.g. `client/nodes/0.js`) */ imports: string[]; @@ -1849,7 +1852,7 @@ declare module '@sveltejs/kit' { stylesheets: string[]; /** external font files that are loaded on the client */ fonts: string[]; - /** inlined styles */ + /** inlined styles. */ inline_styles?(): MaybePromise>; universal: {