@rsvelte/vite-plugin-svelte 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,618 @@
1
+ import process from 'node:process';
2
+ import * as vite from 'vite';
3
+ const {
4
+ defaultClientMainFields,
5
+ defaultServerMainFields,
6
+ defaultClientConditions,
7
+ defaultServerConditions,
8
+ normalizePath,
9
+ searchForWorkspaceRoot,
10
+ // @ts-ignore
11
+ rolldownVersion
12
+ } = vite;
13
+ import { log } from './log.js';
14
+ import { loadSvelteConfig } from './load-svelte-config.js';
15
+ import {
16
+ DEFAULT_SVELTE_EXT,
17
+ FAQ_LINK_MISSING_EXPORTS_CONDITION,
18
+ SVELTE_EXPORT_CONDITIONS,
19
+ SVELTE_IMPORTS,
20
+ SVELTE_RUNTIME_DEPENDENCIES
21
+ } from './constants.js';
22
+
23
+ import path from 'node:path';
24
+ import { addExtraPreprocessors } from './preprocess.js';
25
+ import deepmerge from 'deepmerge';
26
+ import {
27
+ crawlFrameworkPkgs,
28
+ isDepExcluded,
29
+ isDepExternaled,
30
+ isDepIncluded,
31
+ isDepNoExternaled
32
+ } from 'vitefu';
33
+
34
+ import { isCommonDepWithoutSvelteField } from './dependencies.js';
35
+
36
+ const allowedPluginOptions = new Set([
37
+ 'include',
38
+ 'exclude',
39
+ 'emitCss',
40
+ 'hot',
41
+ 'ignorePluginPreprocessors',
42
+ 'disableDependencyReinclusion',
43
+ 'prebundleSvelteLibraries',
44
+ 'inspector',
45
+ 'dynamicCompileOptions',
46
+ 'experimental'
47
+ ]);
48
+
49
+ const knownRootOptions = new Set(['extensions', 'compilerOptions', 'preprocess', 'onwarn']);
50
+
51
+ const allowedInlineOptions = new Set(['configFile', ...allowedPluginOptions, ...knownRootOptions]);
52
+
53
+ /**
54
+ * @param {Partial<import('../public.d.ts').Options>} [inlineOptions]
55
+ */
56
+ export function validateInlineOptions(inlineOptions) {
57
+ const invalidKeys = Object.keys(inlineOptions || {}).filter(
58
+ (key) => !allowedInlineOptions.has(key)
59
+ );
60
+ if (invalidKeys.length) {
61
+ log.warn(`invalid plugin options "${invalidKeys.join(', ')}" in inline config`, inlineOptions);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * @param {Partial<import('../public.d.ts').SvelteConfig>} [config]
67
+ * @returns {Partial<import('../public.d.ts').Options> | undefined}
68
+ */
69
+ function convertPluginOptions(config) {
70
+ if (!config) {
71
+ return;
72
+ }
73
+ const invalidRootOptions = Object.keys(config).filter((key) => allowedPluginOptions.has(key));
74
+ if (invalidRootOptions.length > 0) {
75
+ throw new Error(
76
+ `Invalid options in svelte config. Move the following options into 'vitePlugin:{...}': ${invalidRootOptions.join(
77
+ ', '
78
+ )}`
79
+ );
80
+ }
81
+ if (!config.vitePlugin) {
82
+ return config;
83
+ }
84
+ const pluginOptions = config.vitePlugin;
85
+ const pluginOptionKeys = Object.keys(pluginOptions);
86
+
87
+ const rootOptionsInPluginOptions = pluginOptionKeys.filter((key) => knownRootOptions.has(key));
88
+ if (rootOptionsInPluginOptions.length > 0) {
89
+ throw new Error(
90
+ `Invalid options in svelte config under vitePlugin:{...}', move them to the config root : ${rootOptionsInPluginOptions.join(
91
+ ', '
92
+ )}`
93
+ );
94
+ }
95
+ const duplicateOptions = pluginOptionKeys.filter((key) =>
96
+ Object.prototype.hasOwnProperty.call(config, key)
97
+ );
98
+ if (duplicateOptions.length > 0) {
99
+ throw new Error(
100
+ `Invalid duplicate options in svelte config under vitePlugin:{...}', they are defined in root too and must only exist once: ${duplicateOptions.join(
101
+ ', '
102
+ )}`
103
+ );
104
+ }
105
+ const unknownPluginOptions = pluginOptionKeys.filter((key) => !allowedPluginOptions.has(key));
106
+ if (unknownPluginOptions.length > 0) {
107
+ log.warn(
108
+ `ignoring unknown plugin options in svelte config under vitePlugin:{...}: ${unknownPluginOptions.join(
109
+ ', '
110
+ )}`
111
+ );
112
+ unknownPluginOptions.forEach((unkownOption) => {
113
+ // @ts-expect-error not typed
114
+ delete pluginOptions[unkownOption];
115
+ });
116
+ }
117
+ /** @type {import('../public.d.ts').Options} */
118
+ const result = {
119
+ ...config,
120
+ ...pluginOptions
121
+ };
122
+ // @ts-expect-error it exists
123
+ delete result.vitePlugin;
124
+
125
+ return result;
126
+ }
127
+
128
+ /**
129
+ * used in config phase, merges the default options, svelte config, and inline options
130
+ * @param {Partial<import('../public.d.ts').Options> | undefined} inlineOptions
131
+ * @param {import('vite').UserConfig} viteUserConfig
132
+ * @param {import('vite').ConfigEnv} viteEnv
133
+ * @returns {Promise<import('../types/options.d.ts').PreResolvedOptions>}
134
+ */
135
+ export async function preResolveOptions(inlineOptions, viteUserConfig, viteEnv) {
136
+ if (!inlineOptions) {
137
+ inlineOptions = {};
138
+ }
139
+ /** @type {import('vite').UserConfig} */
140
+ const viteConfigWithResolvedRoot = {
141
+ ...viteUserConfig,
142
+ root: resolveViteRoot(viteUserConfig)
143
+ };
144
+ const isBuild = viteEnv.command === 'build';
145
+ /** @type {Partial<import('../types/options.d.ts').PreResolvedOptions>} */
146
+ const defaultOptions = {
147
+ extensions: DEFAULT_SVELTE_EXT,
148
+ emitCss: true,
149
+ prebundleSvelteLibraries: !isBuild
150
+ };
151
+ const svelteConfig = convertPluginOptions(
152
+ await loadSvelteConfig(viteConfigWithResolvedRoot, inlineOptions)
153
+ );
154
+ /** @type {Partial<import('../types/options.d.ts').PreResolvedOptions>} */
155
+ const extraOptions = {
156
+ root: viteConfigWithResolvedRoot.root,
157
+ isBuild,
158
+ isServe: viteEnv.command === 'serve',
159
+ isDebug: process.env.DEBUG != null
160
+ };
161
+
162
+ const merged = /** @type {import('../types/options.d.ts').PreResolvedOptions} */ (
163
+ mergeConfigs(defaultOptions, svelteConfig, inlineOptions, extraOptions)
164
+ );
165
+ // configFile of svelteConfig contains the absolute path it was loaded from,
166
+ // prefer it over the possibly relative inline path
167
+ if (svelteConfig?.configFile) {
168
+ merged.configFile = svelteConfig.configFile;
169
+ }
170
+ return merged;
171
+ }
172
+
173
+ /**
174
+ * @template T
175
+ * @param {(Partial<T> | undefined)[]} configs
176
+ * @returns T
177
+ */
178
+ function mergeConfigs(...configs) {
179
+ /** @type {Partial<T>} */
180
+ let result = {};
181
+ for (const config of configs.filter((x) => x != null)) {
182
+ result = deepmerge(result, /** @type {Partial<T>} */ (config), {
183
+ // replace arrays
184
+ arrayMerge: (target, source) => source ?? target
185
+ });
186
+ }
187
+ return /** @type {T} */ result;
188
+ }
189
+
190
+ /**
191
+ * used in configResolved phase, merges a contextual default config, pre-resolved options, and some preprocessors. also validates the final config.
192
+ *
193
+ * @param {import('../types/options.d.ts').PreResolvedOptions} preResolveOptions
194
+ * @param {import('vite').ResolvedConfig} viteConfig
195
+ * @returns {import('../types/options.d.ts').ResolvedOptions}
196
+ */
197
+ export function resolveOptions(preResolveOptions, viteConfig) {
198
+ const css = preResolveOptions.emitCss ? 'external' : 'injected';
199
+ /** @type {Partial<import('../public.d.ts').Options>} */
200
+ const defaultOptions = {
201
+ compilerOptions: {
202
+ css,
203
+ dev: !viteConfig.isProduction,
204
+ hmr:
205
+ !viteConfig.isProduction &&
206
+ !preResolveOptions.isBuild &&
207
+ viteConfig.server &&
208
+ viteConfig.server.hmr !== false
209
+ }
210
+ };
211
+
212
+ /** @type {Partial<import('../types/options.d.ts').ResolvedOptions>} */
213
+ const extraOptions = {
214
+ root: viteConfig.root,
215
+ isProduction: viteConfig.isProduction
216
+ };
217
+ const merged = /** @type {import('../types/options.d.ts').ResolvedOptions}*/ (
218
+ mergeConfigs(defaultOptions, preResolveOptions, extraOptions)
219
+ );
220
+
221
+ removeIgnoredOptions(merged);
222
+ handleDeprecatedOptions(merged);
223
+ addExtraPreprocessors(merged, viteConfig);
224
+ enforceOptionsForHmr(merged, viteConfig);
225
+ enforceOptionsForProduction(merged);
226
+
227
+ return merged;
228
+ }
229
+
230
+ /**
231
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
232
+ * @param {import('vite').ResolvedConfig} viteConfig
233
+ */
234
+ function enforceOptionsForHmr(options, viteConfig) {
235
+ if (options.hot) {
236
+ log.warn(
237
+ 'svelte 5 has hmr integrated in core. Please remove the vitePlugin.hot option and use compilerOptions.hmr instead'
238
+ );
239
+ delete options.hot;
240
+ options.compilerOptions.hmr = true;
241
+ }
242
+ if (options.compilerOptions.hmr && viteConfig.server?.hmr === false) {
243
+ log.warn(
244
+ 'vite config server.hmr is false but compilerOptions.hmr is true. Forcing compilerOptions.hmr to false as it would not work.'
245
+ );
246
+ options.compilerOptions.hmr = false;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
252
+ */
253
+ function enforceOptionsForProduction(options) {
254
+ if (options.isProduction) {
255
+ if (options.compilerOptions.hmr) {
256
+ log.warn(
257
+ 'you are building for production but compilerOptions.hmr is true, forcing it to false'
258
+ );
259
+ options.compilerOptions.hmr = false;
260
+ }
261
+ if (options.compilerOptions.dev) {
262
+ log.warn(
263
+ 'you are building for production but compilerOptions.dev is true, forcing it to false'
264
+ );
265
+ options.compilerOptions.dev = false;
266
+ }
267
+ }
268
+ }
269
+
270
+ /**
271
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
272
+ */
273
+ function removeIgnoredOptions(options) {
274
+ const ignoredCompilerOptions = ['generate', 'format', 'filename'];
275
+ if (options.compilerOptions.hmr && options.emitCss) {
276
+ ignoredCompilerOptions.push('cssHash');
277
+ }
278
+ const passedCompilerOptions = Object.keys(options.compilerOptions || {});
279
+ const passedIgnored = passedCompilerOptions.filter((o) => ignoredCompilerOptions.includes(o));
280
+ if (passedIgnored.length) {
281
+ log.warn(
282
+ `The following Svelte compilerOptions are controlled by vite-plugin-svelte and essential to its functionality. User-specified values are ignored. Please remove them from your configuration: ${passedIgnored.join(
283
+ ', '
284
+ )}`
285
+ );
286
+ passedIgnored.forEach((ignored) => {
287
+ // @ts-expect-error string access
288
+ delete options.compilerOptions[ignored];
289
+ });
290
+ }
291
+ }
292
+
293
+ /**
294
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
295
+ */
296
+ function handleDeprecatedOptions(options) {
297
+ const experimental = /** @type {Record<string, any>} */ (options.experimental);
298
+ if (experimental) {
299
+ for (const promoted of ['prebundleSvelteLibraries', 'inspector', 'dynamicCompileOptions']) {
300
+ if (experimental[promoted]) {
301
+ //@ts-expect-error untyped assign
302
+ options[promoted] = experimental[promoted];
303
+ delete experimental[promoted];
304
+ log.warn(
305
+ `Option "experimental.${promoted}" is no longer experimental and has moved to "${promoted}". Please update your Svelte or Vite config.`
306
+ );
307
+ }
308
+ }
309
+ if (experimental.generateMissingPreprocessorSourcemaps) {
310
+ log.warn('experimental.generateMissingPreprocessorSourcemaps has been removed.');
311
+ }
312
+ }
313
+ }
314
+
315
+ /**
316
+ * vite passes unresolved `root`option to config hook but we need the resolved value, so do it here
317
+ *
318
+ * @see https://github.com/sveltejs/vite-plugin-svelte/issues/113
319
+ * @see https://github.com/vitejs/vite/blob/43c957de8a99bb326afd732c962f42127b0a4d1e/packages/vite/src/node/config.ts#L293
320
+ *
321
+ * @param {import('vite').UserConfig} viteConfig
322
+ * @returns {string | undefined}
323
+ */
324
+ function resolveViteRoot(viteConfig) {
325
+ return normalizePath(viteConfig.root ? path.resolve(viteConfig.root) : process.cwd());
326
+ }
327
+
328
+ /**
329
+ * @param {import('../types/options.d.ts').PreResolvedOptions} options
330
+ * @param {import('vite').UserConfig} config
331
+ * @returns {Promise<Partial<import('vite').UserConfig>>}
332
+ */
333
+ export async function buildExtraViteConfig(options, config) {
334
+ /** @type {Partial<import('vite').UserConfig>} */
335
+ const extraViteConfig = {
336
+ resolve: {
337
+ dedupe: [...SVELTE_IMPORTS]
338
+ }
339
+ // this option is still awaiting a PR in vite to be supported
340
+ // see https://github.com/sveltejs/vite-plugin-svelte/issues/60
341
+ // knownJsSrcExtensions: options.extensions
342
+ };
343
+
344
+ const extraSvelteConfig = buildExtraConfigForSvelte(config);
345
+ const extraDepsConfig = await buildExtraConfigForDependencies(options, config);
346
+ // merge extra svelte and deps config, but make sure dep values are not contradicting svelte
347
+ extraViteConfig.optimizeDeps = {
348
+ include: [
349
+ ...extraSvelteConfig.optimizeDeps.include,
350
+ ...extraDepsConfig.optimizeDeps.include.filter(
351
+ (dep) => !isDepExcluded(dep, extraSvelteConfig.optimizeDeps.exclude)
352
+ )
353
+ ],
354
+ exclude: [
355
+ ...extraSvelteConfig.optimizeDeps.exclude,
356
+ ...extraDepsConfig.optimizeDeps.exclude.filter(
357
+ (dep) => !isDepIncluded(dep, extraSvelteConfig.optimizeDeps.include)
358
+ )
359
+ ]
360
+ };
361
+
362
+ extraViteConfig.ssr = {
363
+ external: [
364
+ ...extraSvelteConfig.ssr.external,
365
+ ...extraDepsConfig.ssr.external.filter(
366
+ (dep) => !isDepNoExternaled(dep, extraSvelteConfig.ssr.noExternal)
367
+ )
368
+ ],
369
+ noExternal: [
370
+ ...extraSvelteConfig.ssr.noExternal,
371
+ ...extraDepsConfig.ssr.noExternal.filter(
372
+ (dep) => !isDepExternaled(dep, extraSvelteConfig.ssr.external)
373
+ )
374
+ ]
375
+ };
376
+
377
+ // enable hmrPartialAccept if not explicitly disabled
378
+ if (config.experimental?.hmrPartialAccept !== false) {
379
+ log.debug('enabling "experimental.hmrPartialAccept" in vite config', undefined, 'config');
380
+ extraViteConfig.experimental = { hmrPartialAccept: true };
381
+ }
382
+ validateViteConfig(extraViteConfig, config, options);
383
+ return extraViteConfig;
384
+ }
385
+
386
+ /**
387
+ * @param {Partial<import('vite').UserConfig>} extraViteConfig
388
+ * @param {import('vite').UserConfig} config
389
+ * @param {import('../types/options.d.ts').PreResolvedOptions} options
390
+ */
391
+ function validateViteConfig(extraViteConfig, config, options) {
392
+ const { prebundleSvelteLibraries, isBuild } = options;
393
+ if (prebundleSvelteLibraries) {
394
+ /** @type {(option: 'dev' | 'build' | boolean)=> boolean} */
395
+ const isEnabled = (option) => option !== true && option !== (isBuild ? 'build' : 'dev');
396
+ /** @type {(name: string, value: 'dev' | 'build' | boolean, recommendation: string)=> void} */
397
+ const logWarning = (name, value, recommendation) =>
398
+ log.warn.once(
399
+ `Incompatible options: \`prebundleSvelteLibraries: true\` and vite \`${name}: ${JSON.stringify(
400
+ value
401
+ )}\` ${isBuild ? 'during build.' : '.'} ${recommendation}`
402
+ );
403
+ const viteOptimizeDepsDisabled = config.optimizeDeps?.disabled ?? 'build'; // fall back to vite default
404
+ const isOptimizeDepsEnabled = isEnabled(viteOptimizeDepsDisabled);
405
+ if (!isBuild && !isOptimizeDepsEnabled) {
406
+ logWarning(
407
+ 'optimizeDeps.disabled',
408
+ viteOptimizeDepsDisabled,
409
+ 'Forcing `optimizeDeps.disabled: "build"`. Disable prebundleSvelteLibraries or update your vite config to enable optimizeDeps during dev.'
410
+ );
411
+ if (!extraViteConfig.optimizeDeps) {
412
+ extraViteConfig.optimizeDeps = {};
413
+ }
414
+ extraViteConfig.optimizeDeps.disabled = 'build';
415
+ } else if (isBuild && isOptimizeDepsEnabled) {
416
+ logWarning(
417
+ 'optimizeDeps.disabled',
418
+ viteOptimizeDepsDisabled,
419
+ 'Disable optimizeDeps or prebundleSvelteLibraries for build if you experience errors.'
420
+ );
421
+ }
422
+ }
423
+ if (rolldownVersion && isBuild) {
424
+ // read user config inlineConst value
425
+ const inlineConst =
426
+ //@ts-ignore optimization only exists in vite-8 beta
427
+ config.build?.rolldownOptions?.optimization?.inlineConst ??
428
+ //@ts-ignore optimization only exists in vite-8 beta
429
+ config.build?.rollupOptions?.optimization?.inlineConst;
430
+
431
+ if (inlineConst === false) {
432
+ log.warn(
433
+ 'Your rolldown config contains `optimization.inlineConst: false`. This can lead to increased bundle size and leaked server code in client build.'
434
+ );
435
+ }
436
+ }
437
+ }
438
+
439
+ /**
440
+ * @param {import('../types/options.d.ts').PreResolvedOptions} options
441
+ * @param {import('vite').UserConfig} config
442
+ * @returns {Promise<import('vitefu').CrawlFrameworkPkgsResult>}
443
+ */
444
+ async function buildExtraConfigForDependencies(options, config) {
445
+ // extra handling for svelte dependencies in the project
446
+ const packagesWithoutSvelteExportsCondition = new Set();
447
+ const depsConfig = await crawlFrameworkPkgs({
448
+ root: options.root,
449
+ workspaceRoot: searchForWorkspaceRoot(options.root),
450
+ isBuild: options.isBuild,
451
+ viteUserConfig: config,
452
+ isFrameworkPkgByJson(pkgJson) {
453
+ let hasSvelteCondition = false;
454
+ if (typeof pkgJson.exports === 'object') {
455
+ // use replacer as a simple way to iterate over nested keys
456
+ JSON.stringify(pkgJson.exports, (key, value) => {
457
+ if (SVELTE_EXPORT_CONDITIONS.includes(key)) {
458
+ hasSvelteCondition = true;
459
+ }
460
+ return value;
461
+ });
462
+ }
463
+ const hasSvelteField = !!pkgJson.svelte;
464
+ if (hasSvelteField && !hasSvelteCondition) {
465
+ packagesWithoutSvelteExportsCondition.add(`${pkgJson.name}@${pkgJson.version}`);
466
+ }
467
+ return hasSvelteCondition || hasSvelteField;
468
+ },
469
+ isSemiFrameworkPkgByJson(pkgJson) {
470
+ return !!pkgJson.dependencies?.svelte || !!pkgJson.peerDependencies?.svelte;
471
+ },
472
+ isFrameworkPkgByName(pkgName) {
473
+ const isNotSveltePackage = isCommonDepWithoutSvelteField(pkgName);
474
+ if (isNotSveltePackage) {
475
+ return false;
476
+ } else {
477
+ return undefined;
478
+ }
479
+ }
480
+ });
481
+ if (
482
+ !options.isBuild &&
483
+ !options.experimental?.disableSvelteResolveWarnings &&
484
+ packagesWithoutSvelteExportsCondition?.size > 0
485
+ ) {
486
+ log.info.once(
487
+ `The following packages have a svelte field in their package.json but no exports condition for svelte.\n\n${[
488
+ ...packagesWithoutSvelteExportsCondition
489
+ ].join('\n')}\n\nPlease see ${FAQ_LINK_MISSING_EXPORTS_CONDITION} for details.`
490
+ );
491
+ }
492
+ log.debug('extra config for dependencies generated by vitefu', depsConfig, 'config');
493
+
494
+ if (options.prebundleSvelteLibraries) {
495
+ // prebundling enabled, so we don't need extra dependency excludes
496
+ depsConfig.optimizeDeps.exclude = [];
497
+ // but keep dependency reinclusions of explicit user excludes
498
+ const userExclude = config.optimizeDeps?.exclude;
499
+ depsConfig.optimizeDeps.include = !userExclude
500
+ ? []
501
+ : depsConfig.optimizeDeps.include.filter((dep) => {
502
+ // reincludes look like this: foo > bar > baz
503
+ // in case foo or bar are excluded, we have to retain the reinclude even with prebundling
504
+ return (
505
+ dep.includes('>') &&
506
+ dep
507
+ .split('>')
508
+ .slice(0, -1)
509
+ .some((d) => isDepExcluded(d.trim(), userExclude))
510
+ );
511
+ });
512
+ }
513
+ if (options.disableDependencyReinclusion === true) {
514
+ depsConfig.optimizeDeps.include = depsConfig.optimizeDeps.include.filter(
515
+ (dep) => !dep.includes('>')
516
+ );
517
+ } else if (Array.isArray(options.disableDependencyReinclusion)) {
518
+ const disabledDeps = options.disableDependencyReinclusion;
519
+ depsConfig.optimizeDeps.include = depsConfig.optimizeDeps.include.filter((dep) => {
520
+ if (!dep.includes('>')) return true;
521
+ const trimDep = dep.replace(/\s+/g, '');
522
+ return disabledDeps.some((disabled) => trimDep.includes(`${disabled}>`));
523
+ });
524
+ }
525
+
526
+ log.debug('post-processed extra config for dependencies', depsConfig, 'config');
527
+
528
+ return depsConfig;
529
+ }
530
+
531
+ /**
532
+ * @param {import('vite').UserConfig} config
533
+ * @returns {import('vite').UserConfig & { optimizeDeps: { include: string[], exclude:string[] }, ssr: { noExternal:(string|RegExp)[], external: string[] } } }
534
+ */
535
+ function buildExtraConfigForSvelte(config) {
536
+ // include svelte imports for optimization unless explicitly excluded
537
+ /** @type {string[]} */
538
+ const include = [];
539
+ /** @type {string[]} */
540
+ const exclude = [];
541
+ if (!isDepExcluded('svelte', config.optimizeDeps?.exclude ?? [])) {
542
+ const svelteImportsToInclude = SVELTE_IMPORTS.filter(
543
+ (si) => !(si.endsWith('/server') || si.includes('/server/'))
544
+ );
545
+ svelteImportsToInclude.push(...SVELTE_RUNTIME_DEPENDENCIES.map((dep) => `svelte > ${dep}`));
546
+ log.debug(
547
+ `adding bare svelte packages and runtime dependencies to optimizeDeps.include: ${svelteImportsToInclude.join(', ')} `,
548
+ undefined,
549
+ 'config'
550
+ );
551
+ include.push(...svelteImportsToInclude);
552
+ } else {
553
+ log.debug(
554
+ '"svelte" is excluded in optimizeDeps.exclude, skipped adding it to include.',
555
+ undefined,
556
+ 'config'
557
+ );
558
+ }
559
+ /** @type {(string | RegExp)[]} */
560
+ const noExternal = [];
561
+ /** @type {string[]} */
562
+ const external = [];
563
+ // add svelte to ssr.noExternal unless it is present in ssr.external
564
+ // so it is correctly resolving according to the conditions in sveltes exports map
565
+ if (!isDepExternaled('svelte', config.ssr?.external ?? [])) {
566
+ noExternal.push('svelte', /^svelte\//);
567
+ }
568
+ // esm-env needs to be bundled by default for the development/production condition
569
+ // be properly used by svelte
570
+ if (!isDepExternaled('esm-env', config.ssr?.external ?? [])) {
571
+ noExternal.push('esm-env');
572
+ }
573
+ return { optimizeDeps: { include, exclude }, ssr: { noExternal, external } };
574
+ }
575
+
576
+ /**
577
+ * Mutates `config` to ensure `resolve.mainFields` is set. If unset, it emulates Vite's default fallback.
578
+ * @param {string} name
579
+ * @param {import('vite').EnvironmentOptions} config
580
+ * @param {{ isSsrTargetWebworker?: boolean }} opts
581
+ */
582
+ export function ensureConfigEnvironmentMainFields(name, config, opts) {
583
+ config.resolve ??= {};
584
+ if (config.resolve.mainFields == null) {
585
+ if (config.consumer === 'client' || name === 'client' || opts.isSsrTargetWebworker) {
586
+ config.resolve.mainFields = [...defaultClientMainFields];
587
+ } else {
588
+ config.resolve.mainFields = [...defaultServerMainFields];
589
+ }
590
+ }
591
+ return true;
592
+ }
593
+
594
+ /**
595
+ * Mutates `config` to ensure `resolve.conditions` is set. If unset, it emulates Vite's default fallback.
596
+ * @param {string} name
597
+ * @param {import('vite').EnvironmentOptions} config
598
+ * @param {{ isSsrTargetWebworker?: boolean }} opts
599
+ */
600
+ export function ensureConfigEnvironmentConditions(name, config, opts) {
601
+ config.resolve ??= {};
602
+ if (config.resolve.conditions == null) {
603
+ if (config.consumer === 'client' || name === 'client' || opts.isSsrTargetWebworker) {
604
+ config.resolve.conditions = [...defaultClientConditions];
605
+ } else {
606
+ config.resolve.conditions = [...defaultServerConditions];
607
+ }
608
+ }
609
+ }
610
+
611
+ /**
612
+ * @template T
613
+ * @param {T | T[] | null | undefined } value
614
+ * @returns {T[]}
615
+ */
616
+ export function arraify(value) {
617
+ return value == null ? [] : Array.isArray(value) ? value : [value];
618
+ }
@@ -0,0 +1,89 @@
1
+ import { log } from './log.js';
2
+ import { LINK_TRANSFORM_WITH_PLUGIN } from './constants.js';
3
+
4
+ /**
5
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
6
+ * @param {import('vite').ResolvedConfig} config
7
+ * @returns {{
8
+ * prependPreprocessors: import('@rsvelte/compiler').PreprocessorGroup[],
9
+ * appendPreprocessors: import('@rsvelte/compiler').PreprocessorGroup[]
10
+ * }}
11
+ */
12
+ function buildExtraPreprocessors(options, config) {
13
+ /** @type {import('@rsvelte/compiler').PreprocessorGroup[]} */
14
+ const prependPreprocessors = [];
15
+ /** @type {import('@rsvelte/compiler').PreprocessorGroup[]} */
16
+ const appendPreprocessors = [];
17
+
18
+ /** @type {import('vite').Plugin[]} */
19
+ const pluginsWithPreprocessors = config.plugins.filter((p) => p?.api?.sveltePreprocess);
20
+
21
+ if (
22
+ !options.isBuild &&
23
+ !options.experimental?.disableApiSveltePreprocessWarnings &&
24
+ pluginsWithPreprocessors.length > 0
25
+ ) {
26
+ log.info.once(
27
+ `The following plugins use the deprecated 'plugin.api.sveltePreprocess' field: ${pluginsWithPreprocessors
28
+ .map((p) => p.name)
29
+ .join(', ')}
30
+ Please contact their maintainers and ask them to use a vite plugin transform instead.
31
+ See ${LINK_TRANSFORM_WITH_PLUGIN} for more information.
32
+ `.replace(/\t+/g, '\t')
33
+ );
34
+ }
35
+ /** @type {import('vite').Plugin[]} */
36
+ const ignored = [];
37
+ /** @type {import('vite').Plugin[]} */
38
+ const included = [];
39
+ for (const p of pluginsWithPreprocessors) {
40
+ if (
41
+ options.ignorePluginPreprocessors === true ||
42
+ (Array.isArray(options.ignorePluginPreprocessors) &&
43
+ options.ignorePluginPreprocessors?.includes(p.name))
44
+ ) {
45
+ ignored.push(p);
46
+ } else {
47
+ included.push(p);
48
+ }
49
+ }
50
+ if (ignored.length > 0) {
51
+ log.debug(
52
+ `Ignoring svelte preprocessors defined by these vite plugins: ${ignored
53
+ .map((p) => p.name)
54
+ .join(', ')}`,
55
+ undefined,
56
+ 'preprocess'
57
+ );
58
+ }
59
+ if (included.length > 0) {
60
+ log.debug(
61
+ `Adding svelte preprocessors defined by these vite plugins: ${included
62
+ .map((p) => p.name)
63
+ .join(', ')}`,
64
+ undefined,
65
+ 'preprocess'
66
+ );
67
+ appendPreprocessors.push(...pluginsWithPreprocessors.map((p) => p.api.sveltePreprocess));
68
+ }
69
+
70
+ return { prependPreprocessors, appendPreprocessors };
71
+ }
72
+
73
+ /**
74
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
75
+ * @param {import('vite').ResolvedConfig} config
76
+ */
77
+ export function addExtraPreprocessors(options, config) {
78
+ const { prependPreprocessors, appendPreprocessors } = buildExtraPreprocessors(options, config);
79
+ if (prependPreprocessors.length > 0 || appendPreprocessors.length > 0) {
80
+ if (!options.preprocess) {
81
+ options.preprocess = [...prependPreprocessors, ...appendPreprocessors];
82
+ } else if (Array.isArray(options.preprocess)) {
83
+ options.preprocess.unshift(...prependPreprocessors);
84
+ options.preprocess.push(...appendPreprocessors);
85
+ } else {
86
+ options.preprocess = [...prependPreprocessors, options.preprocess, ...appendPreprocessors];
87
+ }
88
+ }
89
+ }