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

feat: add `bundleStrategy: 'inline' #13193

Merged
merged 35 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
75609be
feat: add `codeSplitJs` option
paoloricciuti Dec 16, 2024
6882d05
fix: only use `manualChunks` when `!codeSplitJS`
paoloricciuti Dec 16, 2024
ebe416a
Create beige-carpets-wave.md
paoloricciuti Dec 16, 2024
7a272d8
chore: regenerate types
paoloricciuti Dec 16, 2024
6ae1413
chore: add option to `options` app for tests
paoloricciuti Dec 16, 2024
07b78e6
chore: move tests from `options` to `options-2` and add test for bundle
paoloricciuti Dec 16, 2024
0923c8c
chore: rename chunk, add css test
paoloricciuti Dec 16, 2024
690e0ed
chore: rename option to `codeSplit`
paoloricciuti Dec 16, 2024
8bd1a45
chore: apply suggestions from code review
paoloricciuti Dec 17, 2024
e858870
this looks unintentional, reverting
Rich-Harris Dec 18, 2024
c43529e
tweak docs
Rich-Harris Dec 18, 2024
aeeb599
DRY out
Rich-Harris Dec 18, 2024
995c9e9
get rid of Promise.all when codeSplit: false
Rich-Harris Dec 18, 2024
a0c654e
types
Rich-Harris Dec 18, 2024
e3cbb24
rename to bundleStrategy
Rich-Harris Dec 18, 2024
35fb0d9
more detail
Rich-Harris Dec 18, 2024
f2e5838
pretty sure this is unused
Rich-Harris Dec 18, 2024
d4cfc47
tweak
Rich-Harris Dec 18, 2024
c486899
small tweaks
Rich-Harris Dec 18, 2024
57538d1
inline strategy
Rich-Harris Dec 18, 2024
841100e
omit modulepreloads when inlining script
Rich-Harris Dec 18, 2024
27c78bd
regenerate
Rich-Harris Dec 18, 2024
b41d006
fix
Rich-Harris Dec 18, 2024
1ac312a
merge main
Rich-Harris Dec 19, 2024
63051f6
changeset
Rich-Harris Dec 19, 2024
5f3044c
regenerate
Rich-Harris Dec 19, 2024
973c09b
fix
Rich-Harris Dec 19, 2024
c4a6356
Merge branch 'main' into inline-app
Rich-Harris Dec 21, 2024
1331a99
jsdoc
dummdidumm Dec 21, 2024
8628476
allow trailing /index.html when serving from filesystem
Rich-Harris Dec 21, 2024
c45f751
treat current directory as base when using hash-based routing
Rich-Harris Dec 21, 2024
e54c140
Merge branch 'inline-app' of github.com:sveltejs/kit into inline-app
Rich-Harris Dec 22, 2024
ba80ea7
make self-contained apps possible
Rich-Harris Dec 22, 2024
54a51c0
lint
Rich-Harris Dec 22, 2024
37c8787
oops
Rich-Harris Dec 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/strange-geese-train.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': minor
---

feat: add `bundleStrategy: 'inline'` option
2 changes: 1 addition & 1 deletion packages/kit/src/core/config/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const options = object(

output: object({
preloadStrategy: list(['modulepreload', 'preload-js', 'preload-mjs']),
bundleStrategy: list(['split', 'single'])
bundleStrategy: list(['split', 'single', 'inline'])
}),

paths: object({
Expand Down
7 changes: 4 additions & 3 deletions packages/kit/src/exports/public.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,14 +499,15 @@ export interface KitConfig {
*/
preloadStrategy?: 'modulepreload' | 'preload-js' | 'preload-mjs';
/**
* If `'split'`, splits the app up into multiple .js/.css files so that they are loaded lazily as the user navigates around the app. This is the default, and is recommended for most scenarios.
* If `'single'`, creates just one .js bundle and one .css file containing code for the entire app.
* - If `'split'`, splits the app up into multiple .js/.css files so that they are loaded lazily as the user navigates around the app. This is the default, and is recommended for most scenarios.
* - If `'single'`, creates just one .js bundle and one .css file containing code for the entire app.
* - If `'inline'`, inlines all JavaScript and CSS of the entire app into the HTML. The result is usable without a server (i.e. you can just open the file in your browser).
*
* When using `'split'`, you can also adjust the bundling behaviour by setting [`output.experimentalMinChunkSize`](https://rollupjs.org/configuration-options/#output-experimentalminchunksize) and [`output.manualChunks`](https://rollupjs.org/configuration-options/#output-manualchunks)inside your Vite config's [`build.rollupOptions`](https://vite.dev/config/build-options.html#build-rollupoptions).
* @default 'split'
* @since 2.13.0
*/
bundleStrategy?: 'split' | 'single';
bundleStrategy?: 'split' | 'single' | 'inline';
};
paths?: {
/**
Expand Down
30 changes: 25 additions & 5 deletions packages/kit/src/exports/vite/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -631,26 +631,30 @@ async function kit({ svelte_config }) {
const client_base =
kit.paths.relative !== false || kit.paths.assets ? './' : kit.paths.base || '/';

const inline = !ssr && svelte_config.kit.output.bundleStrategy === 'inline';
const split = ssr || svelte_config.kit.output.bundleStrategy === 'split';

new_config = {
base: ssr ? assets_base(kit) : client_base,
build: {
copyPublicDir: !ssr,
cssCodeSplit: true,
cssCodeSplit: svelte_config.kit.output.bundleStrategy !== 'inline',
cssMinify: initial_config.build?.minify == null ? true : !!initial_config.build.minify,
// don't use the default name to avoid collisions with 'static/manifest.json'
manifest: '.vite/manifest.json', // TODO: remove this after bumping peer dep to vite 5
outDir: `${out}/${ssr ? 'server' : 'client'}`,
rollupOptions: {
input,
input: inline ? input['bundle'] : input,
output: {
format: 'esm',
format: inline ? 'iife' : 'esm',
name: `__sveltekit_${version_hash}.app`,
entryFileNames: ssr ? '[name].js' : `${prefix}/[name].[hash].${ext}`,
chunkFileNames: ssr ? 'chunks/[name].js' : `${prefix}/chunks/[name].[hash].${ext}`,
assetFileNames: `${prefix}/assets/[name].[hash][extname]`,
hoistTransitiveImports: false,
sourcemapIgnoreList,
manualChunks:
svelte_config.kit.output.bundleStrategy === 'single' ? () => 'bundle' : undefined
manualChunks: split ? undefined : () => 'bundle',
inlineDynamicImports: false
},
preserveEntrySignatures: 'strict'
},
Expand Down Expand Up @@ -868,6 +872,22 @@ async function kit({ svelte_config }) {
(chunk) => chunk.type === 'chunk' && chunk.modules[env_dynamic_public]
)
};

if (svelte_config.kit.output.bundleStrategy === 'inline') {
const style = /** @type {import('rollup').OutputAsset} */ (
output.find(
(chunk) =>
chunk.type === 'asset' &&
chunk.names.length === 1 &&
chunk.names[0] === 'style.css'
)
);

build_data.client.inline = {
script: read(`${out}/client/${start.file}`),
style: /** @type {string | undefined} */ (style?.source)
};
}
}

const css = output.filter(
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/src/runtime/client/bundle.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* if `bundleStrategy === 'single'`, this file is used as the entry point */
/* if `bundleStrategy` is 'single' or 'inline', this file is used as the entry point */

import * as kit from './entry.js';

Expand Down
27 changes: 20 additions & 7 deletions packages/kit/src/runtime/client/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,25 @@ export function create_updated_store() {
* - uses hash router and pathname is more than base
* @param {URL} url
* @param {string} base
* @param {boolean} has_pathname_in_hash
* @param {boolean} hash_routing
*/
export function is_external_url(url, base, has_pathname_in_hash) {
return (
url.origin !== origin ||
!url.pathname.startsWith(base) ||
(has_pathname_in_hash && url.pathname !== (base || '/'))
);
export function is_external_url(url, base, hash_routing) {
if (url.origin !== origin || !url.pathname.startsWith(base)) {
return true;
}

if (hash_routing) {
if (url.pathname === base + '/') {
return false;
}

// be lenient if serving from filesystem
if (url.protocol === 'file:' && url.pathname.replace(/\/[^/]+\.html?$/, '') === base) {
return false;
}

return true;
}

return false;
}
61 changes: 38 additions & 23 deletions packages/kit/src/runtime/server/page/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,16 +95,21 @@ export async function render_response({
let base_expression = s(paths.base);

// if appropriate, use relative paths for greater portability
if (paths.relative && !state.prerendering?.fallback) {
const segments = event.url.pathname.slice(paths.base.length).split('/').slice(2);
if (paths.relative) {
if (!state.prerendering?.fallback) {
const segments = event.url.pathname.slice(paths.base.length).split('/').slice(2);

base = segments.map(() => '..').join('/') || '.';
base = segments.map(() => '..').join('/') || '.';

// resolve e.g. '../..' against current location, then remove trailing slash
base_expression = `new URL(${s(base)}, location).pathname.slice(0, -1)`;
// resolve e.g. '../..' against current location, then remove trailing slash
base_expression = `new URL(${s(base)}, location).pathname.slice(0, -1)`;

if (!paths.assets || (paths.assets[0] === '/' && paths.assets !== SVELTE_KIT_ASSETS)) {
assets = base;
if (!paths.assets || (paths.assets[0] === '/' && paths.assets !== SVELTE_KIT_ASSETS)) {
assets = base;
}
} else if (options.hash_routing) {
// we have to assume that we're in the right place
base_expression = "new URL('.', location).pathname.slice(0, -1)";
}
}

Expand Down Expand Up @@ -197,7 +202,7 @@ export async function render_response({
for (const url of node.stylesheets) stylesheets.add(url);
for (const url of node.fonts) fonts.add(url);

if (node.inline_styles) {
if (node.inline_styles && !client.inline) {
Object.entries(await node.inline_styles()).forEach(([k, v]) => inline_styles.set(k, v));
}
}
Expand All @@ -223,6 +228,10 @@ export async function render_response({
return `${assets}/${path}`;
};

if (client.inline?.style) {
head += `\n\t<style>${client.inline.style}</style>`;
}

if (inline_styles.size > 0) {
const content = Array.from(inline_styles.values()).join('\n');

Expand Down Expand Up @@ -293,17 +302,19 @@ export async function render_response({
modulepreloads.add(`${options.app_dir}/env.js`);
}

const included_modulepreloads = Array.from(modulepreloads, (dep) => prefixed(dep)).filter(
(path) => resolve_opts.preload({ type: 'js', path })
);

for (const path of included_modulepreloads) {
// see the kit.output.preloadStrategy option for details on why we have multiple options here
link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
if (options.preload_strategy !== 'modulepreload') {
head += `\n\t\t<link rel="preload" as="script" crossorigin="anonymous" href="${path}">`;
} else if (state.prerendering) {
head += `\n\t\t<link rel="modulepreload" href="${path}">`;
if (!client.inline) {
const included_modulepreloads = Array.from(modulepreloads, (dep) => prefixed(dep)).filter(
(path) => resolve_opts.preload({ type: 'js', path })
);

for (const path of included_modulepreloads) {
// see the kit.output.preloadStrategy option for details on why we have multiple options here
link_header_preloads.add(`<${encodeURI(path)}>; rel="modulepreload"; nopush`);
if (options.preload_strategy !== 'modulepreload') {
head += `\n\t\t<link rel="preload" as="script" crossorigin="anonymous" href="${path}">`;
} else if (state.prerendering) {
head += `\n\t\t<link rel="modulepreload" href="${path}">`;
}
}
}

Expand Down Expand Up @@ -392,15 +403,19 @@ export async function render_response({
args.push(`{\n${indent}\t${hydrate.join(`,\n${indent}\t`)}\n${indent}}`);
}

// `client.app` is a proxy for `bundleStrategy !== 'single'`
const boot = client.app
? `Promise.all([
// `client.app` is a proxy for `bundleStrategy === 'split'`
const boot = client.inline
? `${client.inline.script}

__sveltekit_${options.version_hash}.app.start(${args.join(', ')});`
: client.app
? `Promise.all([
import(${s(prefixed(client.start))}),
import(${s(prefixed(client.app))})
]).then(([kit, app]) => {
kit.start(app, ${args.join(', ')});
});`
: `import(${s(prefixed(client.start))}).then((app) => {
: `import(${s(prefixed(client.start))}).then((app) => {
app.start(${args.join(', ')})
});`;

Expand Down
4 changes: 4 additions & 0 deletions packages/kit/src/types/internal.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ export interface BuildData {
stylesheets: string[];
fonts: string[];
uses_env_dynamic_public: boolean;
inline?: {
script: string;
style: string | undefined;
};
} | null;
server_manifest: import('vite').Manifest;
}
Expand Down
11 changes: 8 additions & 3 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -481,14 +481,15 @@ declare module '@sveltejs/kit' {
*/
preloadStrategy?: 'modulepreload' | 'preload-js' | 'preload-mjs';
/**
* If `'split'`, splits the app up into multiple .js/.css files so that they are loaded lazily as the user navigates around the app. This is the default, and is recommended for most scenarios.
* If `'single'`, creates just one .js bundle and one .css file containing code for the entire app.
* - If `'split'`, splits the app up into multiple .js/.css files so that they are loaded lazily as the user navigates around the app. This is the default, and is recommended for most scenarios.
* - If `'single'`, creates just one .js bundle and one .css file containing code for the entire app.
* - If `'inline'`, inlines all JavaScript and CSS of the entire app into the HTML. The result is usable without a server (i.e. you can just open the file in your browser).
*
* When using `'split'`, you can also adjust the bundling behaviour by setting [`output.experimentalMinChunkSize`](https://rollupjs.org/configuration-options/#output-experimentalminchunksize) and [`output.manualChunks`](https://rollupjs.org/configuration-options/#output-manualchunks)inside your Vite config's [`build.rollupOptions`](https://vite.dev/config/build-options.html#build-rollupoptions).
* @default 'split'
* @since 2.13.0
*/
bundleStrategy?: 'split' | 'single';
bundleStrategy?: 'split' | 'single' | 'inline';
};
paths?: {
/**
Expand Down Expand Up @@ -1666,6 +1667,10 @@ declare module '@sveltejs/kit' {
stylesheets: string[];
fonts: string[];
uses_env_dynamic_public: boolean;
inline?: {
script: string;
style: string | undefined;
};
} | null;
server_manifest: import('vite').Manifest;
}
Expand Down
Loading