Skip to content

Commit bd80911

Browse files
committed
refactor: remove code duplication in optimizer plugins and patch them by reference instead of using Object.assign
1 parent d89d132 commit bd80911

File tree

2 files changed

+112
-201
lines changed

2 files changed

+112
-201
lines changed

packages/vite-plugin-svelte/src/utils/optimizer-plugins.js

Lines changed: 76 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -9,139 +9,106 @@ import { normalize } from './id.js';
99
* @typedef {NonNullable<import('vite').DepOptimizationOptions['esbuildOptions']>} EsbuildOptions
1010
* @typedef {NonNullable<EsbuildOptions['plugins']>[number]} EsbuildPlugin
1111
*/
12-
/*
13-
* @typedef {NonNullable<import('vite').DepOptimizationOptions['rollupOptions']>} RollupOptions
14-
* @typedef {NonNullable<RollupOptions['plugins']>[number]} RolldownPlugin
15-
*/
16-
// TODO type correctly when the above works
1712
/**
18-
* @typedef {{
19-
* name: string,
20-
* options: ()=>void,
21-
* transform?:{
22-
* filter?: {id?:{include?:Array<RegExp>,exclude?:Array<RegExp>}}
23-
* handler: (code: string,id: string)=>Promise<any>
24-
* },
25-
* buildStart?:{handler: ()=>void,}
26-
* buildEnd?:{handler: ()=>void,}
27-
* }} RolldownPlugin
13+
* @typedef {NonNullable<import('vite').Rollup.Plugin>} RollupPlugin
2814
*/
29-
export const facadeOptimizeSveltePluginName = 'vite-plugin-svelte:facade';
30-
export const facadeOptimizeSvelteModulePluginName = 'vite-plugin-svelte-module:facade';
15+
16+
export const optimizeSveltePluginName = 'vite-plugin-svelte:optimize';
17+
export const optimizeSvelteModulePluginName = 'vite-plugin-svelte-module:optimize';
3118

3219
/**
20+
* @param {EsbuildPlugin} plugin
3321
* @param {import('../types/options.d.ts').ResolvedOptions} options
34-
* @returns {EsbuildPlugin}
3522
*/
36-
export function esbuildSveltePlugin(options) {
37-
return {
38-
name: 'vite-plugin-svelte:optimize-svelte',
39-
setup(build) {
40-
// Skip in scanning phase as Vite already handles scanning Svelte files.
41-
// Otherwise this would heavily slow down the scanning phase.
42-
if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
43-
44-
const filter = /\.svelte(?:\?.*)?$/;
45-
/** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
46-
let statsCollection;
47-
build.onStart(() => {
48-
statsCollection = options.stats?.startCollection('prebundle library components', {
49-
logResult: (c) => c.stats.length > 1
50-
});
51-
});
52-
build.onLoad({ filter }, async ({ path: filename }) => {
53-
const code = readFileSync(filename, 'utf8');
54-
try {
55-
const result = await compileSvelte(options, { filename, code }, statsCollection);
56-
const contents = result.map
57-
? result.code + '//# sourceMappingURL=' + result.map.toUrl()
58-
: result.code;
59-
return { contents };
60-
} catch (e) {
61-
return { errors: [toESBuildError(e, options)] };
62-
}
63-
});
64-
build.onEnd(() => {
65-
statsCollection?.finish();
23+
export function patchESBuildOptimizerPlugin(plugin, options) {
24+
const components = plugin.name === optimizeSveltePluginName;
25+
const compileFn = components ? compileSvelte : compileSvelteModule;
26+
const statsName = components ? 'prebundle library components' : 'prebundle library modules';
27+
const filter = components ? /\.svelte(?:\?.*)?$/ : /\.svelte\.[jt]s(?:\?.*)?$/;
28+
plugin.setup = (build) => {
29+
if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
30+
31+
/** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
32+
let statsCollection;
33+
build.onStart(() => {
34+
statsCollection = options.stats?.startCollection(statsName, {
35+
logResult: (c) => c.stats.length > 1
6636
});
67-
}
37+
});
38+
build.onLoad({ filter }, async ({ path: filename }) => {
39+
const code = readFileSync(filename, 'utf8');
40+
try {
41+
const result = await compileFn(options, { filename, code }, statsCollection);
42+
const contents = result.map
43+
? result.code + '//# sourceMappingURL=' + result.map.toUrl()
44+
: result.code;
45+
return { contents };
46+
} catch (e) {
47+
return { errors: [toESBuildError(e, options)] };
48+
}
49+
});
50+
build.onEnd(() => {
51+
statsCollection?.finish();
52+
});
6853
};
6954
}
7055

7156
/**
57+
* @param {RollupPlugin} plugin
7258
* @param {import('../types/options.d.ts').ResolvedOptions} options
73-
* @param {boolean} components
74-
* @returns {RolldownPlugin}
7559
*/
76-
function createOptimizerPlugin(options, components = true) {
60+
export function patchRolldownOptimizerPlugin(plugin, options) {
61+
const components = plugin.name === optimizeSveltePluginName;
7762
const compileFn = components ? compileSvelte : compileSvelteModule;
78-
const name = components
79-
? 'vite-plugin-svelte:prebundle-components'
80-
: 'vite-plugin-svelte:prebundle-modules';
8163
const statsName = components ? 'prebundle library components' : 'prebundle library modules';
8264
const includeRe = components ? /^[^?#]+\.svelte(?:[?#]|$)/ : /^[^?#]+\.svelte\.[jt]s(?:[?#]|$)/;
8365
/** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
8466
let statsCollection;
85-
/** @type boolean */
86-
let isScanner;
87-
/** @type {RolldownPlugin} */
88-
const plugin = {
89-
name,
90-
// @ts-expect-error not typed in rolldown yet
91-
options(opts) {
92-
// workaround to not run this plugin in scanner, only define hooks in options if there's no scanner plugins in the pipeline
93-
isScanner = opts.plugins?.some(
94-
(/** @type {{ name: string; }} */ p) => p.name === 'vite:dep-scan:resolve'
95-
);
96-
},
97-
transform: {
98-
filter: {
99-
id: {
100-
get include() {
101-
return isScanner ? [/^$/] : [includeRe];
67+
68+
plugin.options = (opts) => {
69+
// @ts-expect-error plugins is an array here
70+
const isScanner = opts.plugins.some(
71+
(/** @type {{ name: string; }} */ p) => p.name === 'vite:dep-scan:resolve'
72+
);
73+
if (isScanner) {
74+
delete plugin.buildStart;
75+
delete plugin.transform;
76+
delete plugin.buildEnd;
77+
} else {
78+
plugin.transform = {
79+
filter: { id: includeRe },
80+
/**
81+
* @param {string} code
82+
* @param {string} filename
83+
*/
84+
async handler(code, filename) {
85+
try {
86+
return await compileFn(options, { filename, code }, statsCollection);
87+
} catch (e) {
88+
throw toRollupError(e, options);
10289
}
10390
}
104-
},
105-
/**
106-
* @param {string} code
107-
* @param {string} filename
108-
*/
109-
async handler(code, filename) {
110-
try {
111-
return await compileFn(options, { filename, code }, statsCollection);
112-
} catch (e) {
113-
throw toRollupError(e, options);
114-
}
115-
}
116-
},
117-
buildStart: {
118-
handler: () => {
119-
if (isScanner) {
120-
return;
91+
};
92+
plugin.buildStart = {
93+
handler: () => {
94+
if (isScanner) {
95+
return;
96+
}
97+
statsCollection = options.stats?.startCollection(statsName, {
98+
logResult: (c) => c.stats.length > 1
99+
});
121100
}
122-
statsCollection = options.stats?.startCollection(statsName, {
123-
logResult: (c) => c.stats.length > 1
124-
});
125-
}
126-
},
127-
buildEnd: {
128-
handler: () => {
129-
if (isScanner) {
130-
return;
101+
};
102+
plugin.buildEnd = {
103+
handler: () => {
104+
if (isScanner) {
105+
return;
106+
}
107+
statsCollection?.finish();
131108
}
132-
statsCollection?.finish();
133-
}
109+
};
134110
}
135111
};
136-
return plugin;
137-
}
138-
139-
/**
140-
* @param {import('../types/options.d.ts').ResolvedOptions} options
141-
* @returns {RolldownPlugin}
142-
*/
143-
export function rolldownOptimizeSveltePlugin(options) {
144-
return createOptimizerPlugin(options, true);
145112
}
146113

147114
/**
@@ -215,53 +182,6 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
215182
};
216183
}
217184

218-
/**
219-
* @param {import('../types/options.d.ts').ResolvedOptions} options
220-
* @returns {EsbuildPlugin}
221-
*/
222-
export function esbuildSvelteModulePlugin(options) {
223-
return {
224-
name: 'vite-plugin-svelte-module:optimize-svelte',
225-
setup(build) {
226-
// Skip in scanning phase as Vite already handles scanning Svelte files.
227-
// Otherwise this would heavily slow down the scanning phase.
228-
if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
229-
230-
const filter = /\.svelte\.[jt]s(?:\?.*)?$/;
231-
/** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
232-
let statsCollection;
233-
build.onStart(() => {
234-
statsCollection = options.stats?.startCollection('prebundle library modules', {
235-
logResult: (c) => c.stats.length > 1
236-
});
237-
});
238-
build.onLoad({ filter }, async ({ path: filename }) => {
239-
const code = readFileSync(filename, 'utf8');
240-
try {
241-
const result = await compileSvelteModule(options, { filename, code }, statsCollection);
242-
const contents = result.map
243-
? result.code + '//# sourceMappingURL=' + result.map.toUrl()
244-
: result.code;
245-
return { contents };
246-
} catch (e) {
247-
return { errors: [toESBuildError(e, options)] };
248-
}
249-
});
250-
build.onEnd(() => {
251-
statsCollection?.finish();
252-
});
253-
}
254-
};
255-
}
256-
257-
/**
258-
* @param {import('../types/options.d.ts').ResolvedOptions} options
259-
* @returns {RolldownPlugin}
260-
*/
261-
export function rolldownOptimizeSvelteModulePlugin(options) {
262-
return createOptimizerPlugin(options, false);
263-
}
264-
265185
/**
266186
* @param {import('../types/options.d.ts').ResolvedOptions} options
267187
* @param {{ filename: string; code: string }} input

packages/vite-plugin-svelte/src/utils/options.js

Lines changed: 36 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ import {
2121

2222
import path from 'node:path';
2323
import {
24-
esbuildSvelteModulePlugin,
25-
esbuildSveltePlugin,
26-
facadeOptimizeSvelteModulePluginName,
27-
facadeOptimizeSveltePluginName,
28-
rolldownOptimizeSvelteModulePlugin,
29-
rolldownOptimizeSveltePlugin
24+
optimizeSvelteModulePluginName,
25+
optimizeSveltePluginName,
26+
patchESBuildOptimizerPlugin,
27+
patchRolldownOptimizerPlugin
3028
} from './optimizer-plugins.js';
3129
import { addExtraPreprocessors } from './preprocess.js';
3230
import deepmerge from 'deepmerge';
@@ -393,27 +391,34 @@ export async function buildExtraViteConfig(options, config) {
393391
// Experimental Vite API to allow these extensions to be scanned and prebundled
394392
extensions: options.extensions ?? ['.svelte']
395393
};
396-
// Add esbuild plugin to prebundle Svelte files.
394+
// Add optimizer plugins to prebundle Svelte files.
397395
// Currently a placeholder as more information is needed after Vite config is resolved,
398-
// the real Svelte plugin is added in `patchResolvedViteConfig()`
396+
// the added plugins are patched in `patchResolvedViteConfig()`
399397
if (rolldownVersion) {
400-
//@ts-expect-error rolldown-vite types not finished
398+
/**
399+
*
400+
* @param {string} name
401+
* @returns {import('vite').Rollup.Plugin}
402+
*/
403+
const placeholderRolldownOptimizerPlugin = (name) => ({
404+
name,
405+
options() {},
406+
buildStart() {},
407+
buildEnd() {},
408+
transform: { filter: { id: /^$/ }, handler() {} }
409+
});
410+
//@ts-expect-error rolldown types not finished
401411
extraViteConfig.optimizeDeps.rollupOptions = {
402412
plugins: [
403-
{ name: facadeOptimizeSveltePluginName, transform() {}, buildStart() {}, buildEnd() {} },
404-
{
405-
name: facadeOptimizeSvelteModulePluginName,
406-
transform() {},
407-
buildStart() {},
408-
buildEnd() {}
409-
}
413+
placeholderRolldownOptimizerPlugin(optimizeSveltePluginName),
414+
placeholderRolldownOptimizerPlugin(optimizeSvelteModulePluginName)
410415
]
411416
};
412417
} else {
413418
extraViteConfig.optimizeDeps.esbuildOptions = {
414419
plugins: [
415-
{ name: facadeOptimizeSveltePluginName, setup: () => {} },
416-
{ name: facadeOptimizeSvelteModulePluginName, setup: () => {} }
420+
{ name: optimizeSveltePluginName, setup: () => {} },
421+
{ name: optimizeSvelteModulePluginName, setup: () => {} }
417422
]
418423
};
419424
}
@@ -615,35 +620,21 @@ export function patchResolvedViteConfig(viteConfig, options) {
615620
}
616621
}
617622
if (rolldownVersion) {
618-
// @ts-expect-error not typed
619-
const plugins = viteConfig.optimizeDeps.rollupOptions.plugins;
620-
if (plugins) {
621-
const facadeSveltePlugin = plugins.find(
622-
(/** @type {{ name: any; }} */ p) => p.name === facadeOptimizeSveltePluginName
623-
);
624-
if (facadeSveltePlugin) {
625-
Object.assign(facadeSveltePlugin, rolldownOptimizeSveltePlugin(options));
626-
}
627-
const facadeSvelteModulePlugin = plugins.find(
628-
(/** @type {{ name: string; }} */ p) => p.name === facadeOptimizeSvelteModulePluginName
629-
);
630-
if (facadeSvelteModulePlugin) {
631-
Object.assign(facadeSvelteModulePlugin, rolldownOptimizeSvelteModulePlugin(options));
632-
}
623+
const plugins =
624+
// @ts-expect-error not typed
625+
viteConfig.optimizeDeps.rollupOptions?.plugins?.filter((p) =>
626+
[optimizeSveltePluginName, optimizeSvelteModulePluginName].includes(p.name)
627+
) ?? [];
628+
for (const plugin of plugins) {
629+
patchRolldownOptimizerPlugin(plugin, options);
633630
}
634631
} else {
635-
const plugins = viteConfig.optimizeDeps.esbuildOptions?.plugins;
636-
if (plugins) {
637-
const facadeSveltePlugin = plugins.find((p) => p.name === facadeOptimizeSveltePluginName);
638-
if (facadeSveltePlugin) {
639-
Object.assign(facadeSveltePlugin, esbuildSveltePlugin(options));
640-
}
641-
const facadeSvelteModulePlugin = plugins.find(
642-
(p) => p.name === facadeOptimizeSvelteModulePluginName
643-
);
644-
if (facadeSvelteModulePlugin) {
645-
Object.assign(facadeSvelteModulePlugin, esbuildSvelteModulePlugin(options));
646-
}
632+
const plugins =
633+
viteConfig.optimizeDeps.esbuildOptions?.plugins?.filter((p) =>
634+
[optimizeSveltePluginName, optimizeSvelteModulePluginName].includes(p.name)
635+
) ?? [];
636+
for (const plugin of plugins) {
637+
patchESBuildOptimizerPlugin(plugin, options);
647638
}
648639
}
649640
}

0 commit comments

Comments
 (0)