Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[breaking] SSR config object #2669

Closed
wants to merge 14 commits into from
5 changes: 5 additions & 0 deletions .changeset/tidy-bags-sparkle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Made the ssr config option an object with two fields: enabled and overridable, to allow more granular control over ssr behavior
9 changes: 7 additions & 2 deletions documentation/docs/14-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ const config = {
serviceWorker: {
files: (filepath) => !/\.DS_STORE/.test(filepath)
},
ssr: true,
ssr: {
enabled: true,
overridable: true
},
target: null,
trailingSlash: 'never',
vite: () => ({})
Expand Down Expand Up @@ -200,7 +203,9 @@ An object containing zero or more of the following values:

### ssr

Enables or disables [server-side rendering](#ssr-and-javascript-ssr) app-wide.
An object containing any of the following:
- `enabled` — Enables or disables [server-side rendering](#ssr-and-javascript-ssr) app-wide.
- `overridable` — Prevents the previous option to be overriden by pages and disables prerendering (SSR at build time). Setting both of these options to `false` turns your app into an [SPA](#appendix-csr-and-spa).

### target

Expand Down
24 changes: 23 additions & 1 deletion documentation/faq/80-integrations.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ See [Vite's `configureServer` docs](https://vitejs.dev/guide/api-plugin.html#con

### How do I use a client-side only library that depends on `document` or `window`?

Vite will attempt to process all imported libraries and may fail when encountering a library that is not compatible with SSR. [This currently occurs even when SSR is disabled](https://github.com/sveltejs/kit/issues/754).
Vite will attempt to process all imported libraries and may fail when encountering a library that is not compatible with SSR.

If you need access to the `document` or `window` variables or otherwise need it to run only on the client-side you can wrap it in a `browser` check:

Expand Down Expand Up @@ -85,6 +85,28 @@ onMount(() => {
});
```

But if your app doesn't need SSR, you can completely disable it with the `ssr` option, and consume the library as usual:

```js
// svelte.config.js
export default {
kit: {
// ...
ssr: {
enabled: false,
overridable: false
}
// ...
}
};

// path/to/page.svelte
import { method } from 'some-browser-only-library';

method('hello world!');

```

### How do I use Firebase?

Please use SDK v9 which provides a modular SDK approach that's currently in beta. The old versions are very difficult to get working especially with SSR and also resulted in a much larger client download size. Even with v9, most users need to set `kit.ssr: false` until [vite#4425](https://github.com/vitejs/vite/issues/4425) and [firebase-js-sdk#4846](https://github.com/firebase/firebase-js-sdk/issues/4846) are solved.
Expand Down
5 changes: 4 additions & 1 deletion packages/kit/src/core/adapt/prerender.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ const REDIRECT = 3;
* }} opts
*/
export async function prerender({ cwd, out, log, config, build_data, fallback, all }) {
if (!config.kit.prerender.enabled && !fallback) {
if (
(!config.kit.ssr.enabled && !config.kit.ssr.overridable) ||
(!config.kit.prerender.enabled && !fallback)
) {
return;
}

Expand Down
4 changes: 4 additions & 0 deletions packages/kit/src/core/adapt/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ suite('prerender', async () => {
routes: join(__dirname, 'fixtures/prerender/src/routes')
},
appDir: '_app',
ssr: {
enabled: true,
overridable: true
},
prerender: {
enabled: true,
entries: ['*']
Expand Down
70 changes: 39 additions & 31 deletions packages/kit/src/core/build/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,32 +259,35 @@ async function build_server(
}
}

const allow_ssr = config.kit.ssr.enabled || config.kit.ssr.overridable;

/** @type {Record<string, { entry: string, css: string[], js: string[], styles: string[] }>} */
const metadata_lookup = {};

manifest.components.forEach((file) => {
const js_deps = new Set();
const css_deps = new Set();

find_deps(file, js_deps, css_deps);

const js = Array.from(js_deps);
const css = Array.from(css_deps);

const styles = config.kit.amp
? Array.from(css_deps).map((url) => {
const resolved = `${output_dir}/client/${config.kit.appDir}/${url}`;
return fs.readFileSync(resolved, 'utf-8');
})
: [];

metadata_lookup[file] = {
entry: client_manifest[file].file,
css,
js,
styles
};
});
if (allow_ssr) {
manifest.components.forEach((file) => {
const js_deps = new Set();
const css_deps = new Set();

find_deps(file, js_deps, css_deps);

const js = Array.from(js_deps);
const css = Array.from(css_deps);

const styles = config.kit.amp
? Array.from(css_deps).map((url) => {
const resolved = `${output_dir}/client/${config.kit.appDir}/${url}`;
return fs.readFileSync(resolved, 'utf-8');
})
: [];

metadata_lookup[file] = {
entry: client_manifest[file].file,
css,
js,
styles
};
});
}

/** @type {Set<string>} */
const entry_js = new Set();
Expand Down Expand Up @@ -346,7 +349,10 @@ async function build_server(
root,
service_worker: ${service_worker_entry_file ? "'/service-worker.js'" : 'null'},
router: ${s(config.kit.router)},
ssr: ${s(config.kit.ssr)},
ssr: {
enabled: ${s(config.kit.ssr.enabled)},
overridable: ${s(config.kit.ssr.overridable)},
},
target: ${s(config.kit.target)},
template,
trailing_slash: ${s(config.kit.trailingSlash)}
Expand Down Expand Up @@ -412,21 +418,23 @@ async function build_server(
externalFetch: hooks.externalFetch || fetch
});

const module_lookup = {
${manifest.components.map(file => `${s(file)}: () => import(${s(app_relative(file))})`)}
};
${allow_ssr ?
`const module_lookup = {
${manifest.components.map((file) => `${s(file)}: () => import(${s(app_relative(file))})`)}
};` : ''}

const metadata_lookup = ${s(metadata_lookup)};
${allow_ssr ? `const metadata_lookup = ${s(metadata_lookup)};` : ''}

async function load_component(file) {
const { entry, css, js, styles } = metadata_lookup[file];
${allow_ssr ?
`const { entry, css, js, styles } = metadata_lookup[file];
return {
module: await module_lookup[file](),
entry: assets + ${s(prefix)} + entry,
css: css.map(dep => assets + ${s(prefix)} + dep),
js: js.map(dep => assets + ${s(prefix)} + dep),
styles
};
};` : 'throw new Error("Cannot use ssr when both config.kit.ssr.enabled and config.kit.ssr.overridable are false")'}
}

export function render(request, {
Expand Down
10 changes: 8 additions & 2 deletions packages/kit/src/core/config/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,10 @@ test('fills in defaults', () => {
pages: undefined
},
router: true,
ssr: true,
ssr: {
enabled: true,
overridable: true
},
target: null,
trailingSlash: 'never'
}
Expand Down Expand Up @@ -150,7 +153,10 @@ test('fills in partial blanks', () => {
pages: undefined
},
router: true,
ssr: true,
ssr: {
enabled: true,
overridable: true
},
target: null,
trailingSlash: 'never'
}
Expand Down
5 changes: 4 additions & 1 deletion packages/kit/src/core/config/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,10 @@ const options = object(
files: fun((filename) => !/\.DS_STORE/.test(filename))
}),

ssr: boolean(true),
ssr: object({
enabled: boolean(true),
overridable: boolean(true)
}),

target: string(null),

Expand Down
5 changes: 4 additions & 1 deletion packages/kit/src/core/config/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ async function testLoadDefaultConfig(path) {
pages: undefined
},
router: true,
ssr: true,
ssr: {
enabled: true,
overridable: true
},
target: null,
trailingSlash: 'never'
}
Expand Down
16 changes: 15 additions & 1 deletion packages/kit/src/runtime/server/page/respond.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ import { coalesce_to_error } from '../../../utils/error.js';
export async function respond(opts) {
const { request, options, state, $session, route } = opts;

if (!options.ssr.enabled && !options.ssr.overridable) {
return await render_response({
branch: [],
$session,
options,
page_config: {
hydrate: true,
router: true,
ssr: false
},
status: 200
});
}

/** @type {Array<SSRNode | undefined>} */
let nodes;

Expand Down Expand Up @@ -227,7 +241,7 @@ export async function respond(opts) {
*/
function get_page_config(leaf, options) {
return {
ssr: 'ssr' in leaf ? !!leaf.ssr : options.ssr,
ssr: 'ssr' in leaf ? !!leaf.ssr : options.ssr.enabled,
router: 'router' in leaf ? !!leaf.router : options.router,
hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate
};
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/server/page/respond_with_error.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export async function respond_with_error({ request, options, state, $session, st
page_config: {
hydrate: options.hydrate,
router: options.router,
ssr: options.ssr
ssr: options.ssr.enabled
},
status,
error,
Expand Down
16 changes: 16 additions & 0 deletions packages/kit/test/apps/spa/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "test-spa",
"private": true,
"version": "0.0.1",
"scripts": {
"dev": "../../../svelte-kit.js dev",
"build": "../../../svelte-kit.js build",
"preview": "../../../svelte-kit.js preview"
},
"devDependencies": {
"@sveltejs/kit": "workspace:*",
"@sveltejs/adapter-node": "workspace:*",
"svelte": "^3.43.0"
},
"type": "module"
}
13 changes: 13 additions & 0 deletions packages/kit/test/apps/spa/src/app.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

%svelte.head%
</head>
<body>
<div id="svelte">%svelte.body%</div>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const root = /** @type {HTMLElement}*/ (document.getElementById('svelte'));
22 changes: 22 additions & 0 deletions packages/kit/test/apps/spa/src/routes/client-code/_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as assert from 'uvu/assert';

/** @type {import('test').TestMaker} */
export default function (test) {
test('page with client only code', '/client-code', async ({ page, js }) => {
if (js) {
await page.waitForSelector('span');
assert.equal(await page.textContent('span'), 'App root is div#svelte');
} else {
assert.ok(await page.evaluate(() => !document.querySelector('span')));
}
});

test('page with client only dependency', '/client-code/dep', async ({ page, js }) => {
if (js) {
await page.waitForSelector('span');
assert.equal(await page.textContent('span'), 'App root is div#svelte');
} else {
assert.ok(await page.evaluate(() => !document.querySelector('span')));
}
});
}
5 changes: 5 additions & 0 deletions packages/kit/test/apps/spa/src/routes/client-code/dep.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import { root } from './_client_dep';
</script>

<span>App root is {root.localName}#{root.id}</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script context="module">
const appRoot = /** @type {HTMLElement}*/ (document.getElementById('svelte'));
</script>

<span>App root is {appRoot.localName}#{appRoot.id}</span>
12 changes: 12 additions & 0 deletions packages/kit/test/apps/spa/svelte.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
ssr: {
enabled: false,
overridable: false
},
target: '#svelte'
}
};

export default config;
5 changes: 4 additions & 1 deletion packages/kit/types/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,10 @@ export interface Config {
serviceWorker?: {
files?(filepath: string): boolean;
};
ssr?: boolean;
ssr?: {
enabled?: boolean;
overridable?: boolean;
};
target?: string;
trailingSlash?: TrailingSlash;
vite?: ViteConfig | (() => ViteConfig);
Expand Down
5 changes: 4 additions & 1 deletion packages/kit/types/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,10 @@ export interface SSRRenderOptions {
root: SSRComponent['default'];
router: boolean;
service_worker?: string;
ssr: boolean;
ssr: {
enabled: boolean;
overridable: boolean;
};
target: string;
template({ head, body }: { head: string; body: string }): string;
trailing_slash: TrailingSlash;
Expand Down