pull/17338/head
Elliott Johnson 2 weeks ago
parent 66f3255ac9
commit 2be25949ee

@ -1,5 +1,5 @@
/** @import { Component } from 'svelte' */ /** @import { Component } from 'svelte' */
/** @import { CspInternal, HydratableContext, RenderOutput, SSRContext, SyncRenderOutput } from './types.js' */ /** @import { CspInternal, HydratableContext, RenderOutput, SSRContext, SyncRenderOutput, Sha256Source } from './types.js' */
/** @import { MaybePromise } from '#shared' */ /** @import { MaybePromise } from '#shared' */
import { async_mode_flag } from '../flags/index.js'; import { async_mode_flag } from '../flags/index.js';
import { abort } from './abort-signal.js'; import { abort } from './abort-signal.js';
@ -521,7 +521,7 @@ export class Renderer {
* @template {Record<string, any>} Props * @template {Record<string, any>} Props
* @param {Component<Props>} component * @param {Component<Props>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: CspInternal }} options * @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: CspInternal }} options
* @returns {Promise<AccumulatedContent & { hashes: { script: string[] } }>} * @returns {Promise<AccumulatedContent & { hashes: { script: Sha256Source[] } }>}
*/ */
static async #render_async(component, options) { static async #render_async(component, options) {
const previous_context = ssr_context; const previous_context = ssr_context;
@ -629,7 +629,7 @@ export class Renderer {
/** /**
* @param {AccumulatedContent} content * @param {AccumulatedContent} content
* @param {Renderer} renderer * @param {Renderer} renderer
* @returns {AccumulatedContent & { hashes: { script: string[] } }} * @returns {AccumulatedContent & { hashes: { script: Sha256Source[] } }}
*/ */
static #close_render(content, renderer) { static #close_render(content, renderer) {
for (const cleanup of renderer.#collect_on_destroy()) { for (const cleanup of renderer.#collect_on_destroy()) {
@ -706,7 +706,7 @@ export class Renderer {
} }
export class SSRState { export class SSRState {
/** @readonly @type {CspInternal & { script_hashes: string[] }} */ /** @readonly @type {CspInternal & { script_hashes: Sha256Source[] }} */
csp; csp;
/** @readonly @type {'sync' | 'async'} */ /** @readonly @type {'sync' | 'async'} */
@ -723,10 +723,10 @@ export class SSRState {
/** /**
* @param {'sync' | 'async'} mode * @param {'sync' | 'async'} mode
* @param {string} [id_prefix] * @param {string} id_prefix
* @param {CspInternal} [csp] * @param {CspInternal} csp
*/ */
constructor(mode, id_prefix = '', csp = { hash: false }) { constructor(mode, id_prefix, csp = { hash: false }) {
this.mode = mode; this.mode = mode;
this.csp = { ...csp, script_hashes: [] }; this.csp = { ...csp, script_hashes: [] };

@ -15,7 +15,7 @@ export interface SSRContext {
element?: Element; element?: Element;
} }
export type Csp = { nonce: string } | { hash: true }; export type Csp = { nonce: string } | { hash: boolean };
export type CspInternal = { nonce?: string; hash: boolean }; export type CspInternal = { nonce?: string; hash: boolean };
@ -37,6 +37,8 @@ export interface RenderContext {
hydratable: HydratableContext; hydratable: HydratableContext;
} }
export type Sha256Source = `sha256-${string}`;
export interface SyncRenderOutput { export interface SyncRenderOutput {
/** HTML that goes into the `<head>` */ /** HTML that goes into the `<head>` */
head: string; head: string;
@ -45,7 +47,7 @@ export interface SyncRenderOutput {
/** HTML that goes somewhere into the `<body>` */ /** HTML that goes somewhere into the `<body>` */
body: string; body: string;
hashes: { hashes: {
script: string[]; script: Sha256Source[];
}; };
} }

@ -1,14 +1,14 @@
/** @import { SvelteComponent } from '../index.js' */ /** @import { SvelteComponent } from '../index.js' */
/** @import { Csp } from '#server' */
import { asClassComponent as as_class_component, createClassComponent } from './legacy-client.js'; import { asClassComponent as as_class_component, createClassComponent } from './legacy-client.js';
import { render } from '../internal/server/index.js'; import { render } from '../internal/server/index.js';
import { async_mode_flag } from '../internal/flags/index.js'; import { async_mode_flag } from '../internal/flags/index.js';
import * as w from '../internal/server/warnings.js';
// By having this as a separate entry point for server environments, we save the client bundle from having to include the server runtime // By having this as a separate entry point for server environments, we save the client bundle from having to include the server runtime
export { createClassComponent }; export { createClassComponent };
/** @typedef {{ head: string, html: string, css: { code: string, map: null }}} LegacyRenderResult */ /** @typedef {{ head: string, html: string, css: { code: string, map: null }; hashes?: { script: `sha256-${string}`[] } }} LegacyRenderResult */
/** /**
* Takes a Svelte 5 component and returns a Svelte 4 compatible component constructor. * Takes a Svelte 5 component and returns a Svelte 4 compatible component constructor.
@ -25,10 +25,10 @@ export { createClassComponent };
*/ */
export function asClassComponent(component) { export function asClassComponent(component) {
const component_constructor = as_class_component(component); const component_constructor = as_class_component(component);
/** @type {(props?: {}, opts?: { $$slots?: {}; context?: Map<any, any>; }) => LegacyRenderResult & PromiseLike<LegacyRenderResult> } */ /** @type {(props?: {}, opts?: { $$slots?: {}; context?: Map<any, any>; csp?: Csp }) => LegacyRenderResult & PromiseLike<LegacyRenderResult> } */
const _render = (props, { context } = {}) => { const _render = (props, { context, csp } = {}) => {
// @ts-expect-error the typings are off, but this will work if the component is compiled in SSR mode // @ts-expect-error the typings are off, but this will work if the component is compiled in SSR mode
const result = render(component, { props, context }); const result = render(component, { props, context, csp });
const munged = Object.defineProperties( const munged = Object.defineProperties(
/** @type {LegacyRenderResult & PromiseLike<LegacyRenderResult>} */ ({}), /** @type {LegacyRenderResult & PromiseLike<LegacyRenderResult>} */ ({}),
@ -65,7 +65,8 @@ export function asClassComponent(component) {
return onfulfilled({ return onfulfilled({
css: munged.css, css: munged.css,
head: result.head, head: result.head,
html: result.body html: result.body,
hashes: result.hashes
}); });
}, onrejected); }, onrejected);
} }

@ -2566,7 +2566,9 @@ declare module 'svelte/server' {
} }
] ]
): RenderOutput; ): RenderOutput;
type Csp = { nonce: string } | { hash: true }; type Csp = { nonce: string } | { hash: boolean };
type Sha256Source = `sha256-${string}`;
interface SyncRenderOutput { interface SyncRenderOutput {
/** HTML that goes into the `<head>` */ /** HTML that goes into the `<head>` */
@ -2576,7 +2578,7 @@ declare module 'svelte/server' {
/** HTML that goes somewhere into the `<body>` */ /** HTML that goes somewhere into the `<body>` */
body: string; body: string;
hashes: { hashes: {
script: string[]; script: Sha256Source[];
}; };
} }

Loading…
Cancel
Save