Skip to content

Commit 7cd6064

Browse files
dominikgdummdidumm
andauthored
feat: experimental rolldown-vite support (#1135)
* add rolldown-vite to test matrix, improve setup for kit-node test * fix: relax warning filter * fix workflow failure filename * feat: support rolldown-vite in vitePreprocess * fix: ignore rolldown-vite only things in type check * feat: add rolldown optimizer plugins instead of esbuild * fix lockfile * refactor: use factory function for 2 optimizer plugins, use options hook info to drive filter * refactor: remove code duplication in optimizer plugins and patch them by reference instead of using Object.assign * chore: add warning log and changeset for experimental support * fix: reduce warning log to single line and ignore in testcase * fix: buildStart and buildEnd hook of patched rolldown optimizer plugins still referenced isScanner * Update .changeset/heavy-dots-repeat.md Co-authored-by: Simon H <[email protected]> --------- Co-authored-by: Simon H <[email protected]>
1 parent 2aa34e2 commit 7cd6064

File tree

23 files changed

+264
-120
lines changed

23 files changed

+264
-120
lines changed

.changeset/heavy-dots-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': major
3+
---
4+
5+
Using the typescript preprocessor now requires a tsconfig.json with verbatimModuleSyntax enabled, eg @tsconfig/svelte

.changeset/huge-lamps-greet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': minor
3+
---
4+
5+
Add experimental support for rolldown-vite

.changeset/hungry-phones-prove.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': minor
3+
---
4+
5+
replace esbuild optimizer with rolldown optimizer if rolldown-vite is used

.github/workflows/ci.yml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,11 @@ jobs:
8787
# baseline test lowest vite and node version
8888
- node: 20.19
8989
os: ubuntu-latest
90-
vite: '6.3.0'
90+
vite: 'baseline'
91+
# future test with rolldown-vite
92+
- node: 24
93+
os: ubuntu-latest
94+
vite: 'rolldown-vite'
9195
steps:
9296
- uses: actions/checkout@v4
9397
- uses: actions/setup-node@v4
@@ -106,9 +110,16 @@ jobs:
106110
cache-dependency-path: '**/pnpm-lock.yaml'
107111
- name: install
108112
run: pnpm install --frozen-lockfile --ignore-scripts
109-
- name: update vite version
110-
if: matrix.vite != 'current'
111-
run: pnpm update -r --no-save vite@${{matrix.vite}}
113+
- name: downgrade vite to baseline
114+
if: matrix.vite == 'baseline'
115+
run: |
116+
pnpm update -r --no-save [email protected]
117+
pnpm ls vite
118+
- name: update vite to rolldown-vite
119+
if: matrix.vite == 'rolldown-vite'
120+
run: |
121+
pnpm update -r --no-save vite@npm:rolldown-vite@latest
122+
pnpm ls rolldown-vite
112123
- name: install playwright chromium
113124
run: pnpm playwright install chromium
114125
- name: run tests

eslint.config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ export default [
1414
'packages/playground/big/src/pages/**', // lots of generated files
1515
'packages/*/types/index.d.ts',
1616
'packages/*/types/index.d.ts.map',
17-
'packages/*/CHANGELOG.md'
17+
'packages/*/CHANGELOG.md',
18+
'packages/e2e-tests/**/logs/**'
1819
]
1920
},
2021
...svelteOrgEslintConfig, // contains setup for svelte and typescript

packages/e2e-tests/_test_dependencies/vite-plugins/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function writeResolvedConfig() {
6363
};
6464
const dir = path.join(config.root, 'logs', 'resolved-configs');
6565
if (!fs.existsSync(dir)) {
66-
fs.mkdirSync(dir);
66+
fs.mkdirSync(dir, { recursive: true });
6767
}
6868
const filename = path.join(dir, `vite.config.${cmd}${config.build.ssr ? '.ssr' : ''}.json`);
6969
fs.writeFileSync(filename, JSON.stringify(serializableConfig, replacer, '\t'), 'utf-8');

packages/e2e-tests/kit-node/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ node_modules
66
#.env
77
.env.*
88
!.env.example
9+
logs

packages/e2e-tests/prebundle-svelte-deps/svelte.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import preprocess from 'svelte-preprocess';
1+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
22
export default {
3-
preprocess: [preprocess()],
3+
preprocess: [vitePreprocess()],
44
vitePlugin: {
55
prebundleSvelteLibraries: true
66
}

packages/e2e-tests/preprocess-with-vite/__tests__/preprocess-with-vite.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ test('should render App', async () => {
77
expect(await getText('#foo-title')).toBe('Styles with stylus blub');
88
expect(await getColor('#foo-title')).toBe('magenta');
99
expect(await getColor('p.note')).toBe('rgb(255, 62, 0)');
10+
expect(await getText('#enum')).toBe('qoox');
1011
});
1112

1213
test('should not mangle code from esbuild pure annotations', async () => {

packages/e2e-tests/preprocess-with-vite/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"devDependencies": {
1111
"@sveltejs/vite-plugin-svelte": "workspace:^",
12+
"@tsconfig/svelte": "^5.0.4",
1213
"sass": "^1.89.1",
1314
"stylus": "^0.64.0",
1415
"svelte": "^5.33.18",

packages/e2e-tests/preprocess-with-vite/src/App.svelte

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@
33
import Bar from './Bar.svelte';
44
export let hello: string;
55
const world: string = 'world'; // edit world and save to see hmr update
6+
enum Baz {
7+
Qoox = 'qoox'
8+
}
9+
let qoox = Baz.Qoox;
610
</script>
711

812
<h1 class="foo">{hello} {world}</h1>
913
<p id="app-scss">This is styled with scss using darken fn</p>
1014
<Foo />
1115
<Bar />
16+
<p id="enum">{qoox}</p>
1217

1318
<style lang="scss">
1419
@use 'sass:color';

packages/e2e-tests/preprocess-with-vite/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import App from './App.svelte';
2-
import { Hello } from './types.js';
2+
import { type Hello } from './types.ts';
33
import { mount } from 'svelte';
44

55
const hello: Hello = 'Hello';
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "@tsconfig/svelte"
3+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
22

33
export default {
4-
preprocess: [vitePreprocess()]
4+
preprocess: [vitePreprocess({ script: true })]
55
};

packages/e2e-tests/scan-deps/__tests__/scan-deps.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { describe, expect, it } from 'vitest';
33
describe('vite import scan', () => {
44
it('should not fail to discover dependencies exported from script module', async () => {
55
// vite logs an error if scan fails but continues, so validate no errors logged
6-
expect(
7-
e2eServer.logs.server.err.length,
8-
`unexpected errors:\n${e2eServer.logs.server.err.join('\n')}`
9-
).toBe(0);
6+
const errorLogs = e2eServer.logs.server.err.filter(
7+
(line) => !line.includes('Support for rolldown-vite in vite-plugin-svelte is experimental')
8+
);
9+
expect(errorLogs.length, `unexpected errors:\n${errorLogs.join('\n')}`).toBe(0);
1010
});
1111
it('should work with exports from module context', async () => {
1212
expect(await getText('#svelte5')).toBe('svelte5');

packages/e2e-tests/vitestSetup.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,11 @@ beforeAll(
141141
if (fs.existsSync(tempViteCache)) {
142142
await fs.rm(tempViteCache, { force: true, recursive: true });
143143
}
144-
145-
await fs.mkdir(path.join(tempDir, 'logs'));
144+
const logsDir = path.join(tempDir, 'logs');
145+
if (fs.existsSync(logsDir)) {
146+
fs.rmSync(logsDir, { recursive: true, force: true });
147+
}
148+
await fs.mkdir(logsDir);
146149
const customServerScript = path.resolve(path.dirname(testPath), 'serve.js');
147150
const defaultServerScript = path.resolve(e2eTestsRoot, 'e2e-server.js');
148151
const hasCustomServer = fs.existsSync(customServerScript);

packages/vite-plugin-svelte/src/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache.js';
2626
import { loadRaw } from './utils/load-raw.js';
2727
import * as svelteCompiler from 'svelte/compiler';
2828
import { SVELTE_VIRTUAL_STYLE_ID_REGEX } from './utils/constants.js';
29+
import * as vite from 'vite';
30+
// @ts-expect-error rolldownVersion
31+
const { version: viteVersion, rolldownVersion } = vite;
2932

3033
/**
3134
* @param {Partial<import('./public.d.ts').Options>} [inlineOptions]
@@ -35,6 +38,12 @@ export function svelte(inlineOptions) {
3538
if (process.env.DEBUG != null) {
3639
log.setLevel('debug');
3740
}
41+
if (rolldownVersion) {
42+
log.warn.once(
43+
`!!! Support for rolldown-vite in vite-plugin-svelte is experimental (rolldown: ${rolldownVersion}, vite: ${viteVersion}) !!!`
44+
);
45+
}
46+
3847
validateInlineOptions(inlineOptions);
3948
const cache = new VitePluginSvelteCache();
4049
// updated in configResolved hook
@@ -140,6 +149,8 @@ export function svelte(inlineOptions) {
140149
css.meta.vite ??= {};
141150
css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default'];
142151
}
152+
css.moduleType = 'css';
153+
143154
return css;
144155
}
145156
}
@@ -194,6 +205,7 @@ export function svelte(inlineOptions) {
194205
}
195206
return {
196207
...compileData.compiled.js,
208+
moduleType: 'js',
197209
meta: {
198210
vite: {
199211
lang: compileData.lang

packages/vite-plugin-svelte/src/preprocess.js

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import process from 'node:process';
2-
import { isCSSRequest, preprocessCSS, resolveConfig, transformWithEsbuild } from 'vite';
2+
import * as vite from 'vite';
33
import { mapToRelative, removeLangSuffix } from './utils/sourcemaps.js';
4-
4+
const {
5+
isCSSRequest,
6+
preprocessCSS,
7+
resolveConfig,
8+
transformWithEsbuild,
9+
//@ts-expect-error rolldown types don't exist
10+
rolldownVersion,
11+
//@ts-expect-error rolldown types don't exist
12+
transformWithOxc
13+
} = vite;
514
/**
615
* @typedef {(code: string, filename: string) => Promise<{ code: string; map?: any; deps?: Set<string> }>} CssTransform
716
*/
@@ -18,7 +27,7 @@ export function vitePreprocess(opts) {
1827
/** @type {import('svelte/compiler').PreprocessorGroup} */
1928
const preprocessor = { name: 'vite-preprocess' };
2029
if (opts?.script === true) {
21-
preprocessor.script = viteScript().script;
30+
preprocessor.script = rolldownVersion ? viteScriptOxc().script : viteScript().script;
2231
}
2332
if (opts?.style !== false) {
2433
const styleOpts = typeof opts?.style == 'object' ? opts?.style : undefined;
@@ -57,6 +66,37 @@ function viteScript() {
5766
};
5867
}
5968

69+
/**
70+
* @returns {{ script: import('svelte/compiler').Preprocessor }}
71+
*/
72+
function viteScriptOxc() {
73+
return {
74+
async script({ attributes, content, filename = '' }) {
75+
const lang = /** @type {string} */ (attributes.lang);
76+
if (!supportedScriptLangs.includes(lang)) return;
77+
const { code, map } = await transformWithOxc(content, filename, {
78+
lang,
79+
target: 'esnext'
80+
// TODO, how to pass tsconfig compilerOptions (or not needed as config is loaded for file
81+
/*tsconfigRaw: {
82+
compilerOptions: {
83+
// svelte typescript needs this flag to work with type imports
84+
importsNotUsedAsValues: 'preserve',
85+
preserveValueImports: true
86+
}
87+
}*/
88+
});
89+
90+
mapToRelative(map, filename);
91+
92+
return {
93+
code,
94+
map
95+
};
96+
}
97+
};
98+
}
99+
60100
/**
61101
* @param {import('vite').ResolvedConfig | import('vite').InlineConfig} config
62102
* @returns {{ style: import('svelte/compiler').Preprocessor }}

packages/vite-plugin-svelte/src/types/compile.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export interface Code {
1414
map?: any;
1515
dependencies?: any[];
1616
hasGlobal?: boolean;
17+
moduleType?: string; //rolldown-vite
1718
meta?: {
1819
vite?: CustomPluginOptionsVite;
1920
};

0 commit comments

Comments
 (0)