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: enable prebundling by default #494

Merged
merged 16 commits into from
Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/calm-rules-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@sveltejs/vite-plugin-svelte': minor
---

enable `prebundleSvelteLibraries` by default to improve page loading for the dev server.

If you are using deep imports for svelte libraries that are now prebundled, update your imports to use the package index instead.

```diff
- import SomeComponent from 'some-library/src/SomeComponent.svelte'
+ import {SomeComponent} from 'some-library'
```

see [FAQ](https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) for more information about prebundleSvelteLibraries and how to tune it.
dominikg marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 6 additions & 2 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,13 @@ A [picomatch pattern](https://github.com/micromatch/picomatch), or array of patt
### prebundleSvelteLibraries

- **Type:** `boolean`
- **Default:** `false`
- **Default:** `true`

Enable support for Vite optimizeDeps to pre-bundle Svelte libraries.
dominikg marked this conversation as resolved.
Show resolved Hide resolved

This option improves page loading for the dev server in most applications when using svelte component libraries.
dominikg marked this conversation as resolved.
Show resolved Hide resolved

Force Vite to pre-bundle Svelte libraries. Setting this `true` should improve initial page load performance, especially when using large Svelte libraries. See the [FAQ](./faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) for details of the pre-bundling implementation.
See the [FAQ](./faq.md#what-is-going-on-with-vite-and-pre-bundling-dependencies) for details and how to fine-tune it for huge libraries.

## Experimental options

Expand Down
37 changes: 36 additions & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,39 @@ For reference, check out [windicss](https://github.com/windicss/vite-plugin-wind

### What is going on with Vite and `Pre-bundling dependencies:`?

Pre-bundling dependencies is an [optimization in Vite](https://vitejs.dev/guide/dep-pre-bundling.html). It is required for CJS dependencies, as Vite's development server only works with ES modules on the client side. Importantly for Svelte libraries and ESM modules, prebundling combines component libraries into a single file to speed up the initial page load. Try setting the [`prebundleSvelteLibraries`](./config.md#prebundleSvelteLibraries) option to `true` to speed things up. This will likely be enabled by default in future version of the plugin.
Pre-bundling dependencies is an [optimization in Vite](https://vitejs.dev/guide/dep-pre-bundling.html).

It is required for CJS dependencies, as Vite's development server only works with ES modules on the client side.
Importantly for Svelte libraries and ESM modules, prebundling combines component libraries into a single file to speed up the initial page load.

For prebundled svelte libraries it is recommended that you do not use deep imports.
dominikg marked this conversation as resolved.
Show resolved Hide resolved

```diff
- import SomeComponent from 'some-library/src/SomeComponent.svelte'
+ import {SomeComponent} from 'some-library'
```

For huge libraries where you only import a few components this can lead to slower first start, as all components have to be compiled once, even if you never use them.
It also slows down re-prebundling, which can happen when vite discovers new dependencies or you change your svelte config.
dominikg marked this conversation as resolved.
Show resolved Hide resolved

In that case, add these huge libraries to optimizeDeps.exclude and use deep imports to import the components you need.
dominikg marked this conversation as resolved.
Show resolved Hide resolved

e.g.

```js
// vite.config.js
export default defineConfig({
optimizeDeps: {
exclude: ['svelte-2000-icons'] // do not pre-bundle svelte-2000-icons
}
});
```

```html
<!-- src/.../SomeSvelte.svelte -->
<script>
// use deep import to avoid loading all components of svelte-2000-icons in the browser
import OneIcon from 'svelte-2000-icons/src/icons/OneIcon.svelte';
</script>
<OneIcon />
```
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
import { getText, isBuild, readVitePrebundleMetadata } from '~utils';
import { getText } from '~utils';

test('should render component imported via svelte field in package.json', async () => {
expect(await getText('#test-id')).toBe('svelte field works');
});

if (!isBuild) {
test('should optimize nested cjs deps of excluded svelte deps', () => {
const metadataFile = readVitePrebundleMetadata();
const metadata = JSON.parse(metadataFile);
const optimizedPaths = Object.keys(metadata.optimized);
expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-nested');
expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-simple');
expect(optimizedPaths).toContain(
'e2e-test-dep-svelte-nested > e2e-test-dep-svelte-simple > e2e-test-dep-cjs-only'
);
});
}
3 changes: 3 additions & 0 deletions packages/e2e-tests/package-json-svelte-field/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { defineConfig } from 'vite';
export default defineConfig(({ command, mode }) => {
return {
plugins: [svelte()],
optimizeDeps: {
exclude: ['e2e-test-dep-scss-only']
},
build: {
// make build faster by skipping transforms and minification
target: 'esnext',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
import { browserLogs, getText, isBuild, readVitePrebundleMetadata } from '~utils';
import {
browserLogs,
editFile,
getText,
isBuild,
readVitePrebundleMetadata,
waitForServerRestartAndReloadPage
} from '~utils';

test('should not have failed requests', async () => {
async function expectPageToWork() {
browserLogs.forEach((msg) => {
expect(msg).not.toMatch('404');
});
});

test('should render Hybrid import', async () => {
expect(await getText('#hybrid .label')).toBe('dependency-import');
});

test('should render Simple import', async () => {
expect(await getText('#hybrid .label')).toBe('dependency-import');
});

test('should render Nested import', async () => {
expect(await getText('#nested #message')).toBe('nested');
expect(await getText('#nested #cjs-and-esm')).toBe('esm');
});

test('should render api-only import', async () => {
expect(await getText('#api-only')).toBe('api loaded: true');
});
}

if (!isBuild) {
test('page works with pre-bundling enabled', async () => {
await expectPageToWork();
});
test('should optimize svelte dependencies', () => {
const metadataFile = readVitePrebundleMetadata();
const metadata = JSON.parse(metadataFile);
Expand All @@ -39,4 +37,30 @@ if (!isBuild) {
const optimizedPaths = Object.keys(metadata.optimized);
expect(optimizedPaths).not.toContain('e2e-test-dep-scss-only');
});

test('page works with pre-bundling disabled', async () => {
editFile('svelte.config.js', (c) =>
c.replace('prebundleSvelteLibraries: true', 'prebundleSvelteLibraries: false')
);
await waitForServerRestartAndReloadPage();
await expectPageToWork();
const metadataFile = readVitePrebundleMetadata();
const metadata = JSON.parse(metadataFile);
const optimizedPaths = Object.keys(metadata.optimized);
expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-simple');
expect(optimizedPaths).not.toContain('e2e-test-dep-svelte-hybrid');

// this is a bit surprising, we always include js-libraries using svelte
expect(optimizedPaths).toContain('e2e-test-dep-svelte-api-only');

expect(optimizedPaths).toContain('e2e-test-dep-svelte-hybrid > e2e-test-dep-cjs-only');
expect(optimizedPaths).toContain('e2e-test-dep-svelte-simple > e2e-test-dep-cjs-only');
expect(optimizedPaths).toContain(
'e2e-test-dep-svelte-nested > e2e-test-dep-svelte-simple > e2e-test-dep-cjs-only'
);
});
} else {
test('page works', async () => {
await expectPageToWork();
});
}
8 changes: 8 additions & 0 deletions packages/e2e-tests/prebundle-svelte-deps/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,13 @@ export default defineConfig({
// make build faster by skipping transforms and minification
target: 'esnext',
minify: false
},
server: {
watch: {
// During tests we edit the files too fast and sometimes chokidar
// misses change events, so enforce polling for consistency
usePolling: true,
interval: 100
}
}
});
9 changes: 6 additions & 3 deletions packages/vite-plugin-svelte/src/utils/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ export async function preResolveOptions(
};
const defaultOptions: Partial<Options> = {
extensions: ['.svelte'],
emitCss: true
emitCss: true,
prebundleSvelteLibraries: true
};
const svelteConfig = convertPluginOptions(
await loadSvelteConfig(viteConfigWithResolvedRoot, inlineOptions)
Expand Down Expand Up @@ -551,9 +552,11 @@ export interface PluginOptions {
disableDependencyReinclusion?: boolean | string[];

/**
* Force Vite to pre-bundle Svelte libraries
* Enable support for vite optimizeDeps to pre-bundle Svelte libraries
dominikg marked this conversation as resolved.
Show resolved Hide resolved
*
* @default false
* to disable pre-bundling for a specific library, add it to optimizeDeps.exclude.
dominikg marked this conversation as resolved.
Show resolved Hide resolved
*
* @default true
dominikg marked this conversation as resolved.
Show resolved Hide resolved
*/
prebundleSvelteLibraries?: boolean;

Expand Down