@sveltejs/vite-plugin-svelte 1.0.0-next.3 → 1.0.0-next.33

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,513 @@
1
+ /* eslint-disable no-unused-vars */
2
+ import {
3
+ ConfigEnv,
4
+ DepOptimizationOptions,
5
+ ResolvedConfig,
6
+ UserConfig,
7
+ ViteDevServer,
8
+ normalizePath
9
+ } from 'vite';
10
+ import { log } from './log';
11
+ import { loadSvelteConfig } from './load-svelte-config';
12
+ import { SVELTE_HMR_IMPORTS, SVELTE_IMPORTS, SVELTE_RESOLVE_MAIN_FIELDS } from './constants';
13
+ // eslint-disable-next-line node/no-missing-import
14
+ import { CompileOptions, Warning } from 'svelte/types/compiler/interfaces';
15
+ import {
16
+ MarkupPreprocessor,
17
+ Preprocessor,
18
+ PreprocessorGroup,
19
+ Processed
20
+ // eslint-disable-next-line node/no-missing-import
21
+ } from 'svelte/types/compiler/preprocess';
22
+ import path from 'path';
23
+ import { findRootSvelteDependencies, needsOptimization, SvelteDependency } from './dependencies';
24
+ import { createRequire } from 'module';
25
+ import { esbuildSveltePlugin, facadeEsbuildSveltePluginName } from './esbuild';
26
+ import { addExtraPreprocessors } from './preprocess';
27
+
28
+ const knownOptions = new Set([
29
+ 'configFile',
30
+ 'include',
31
+ 'exclude',
32
+ 'extensions',
33
+ 'emitCss',
34
+ 'compilerOptions',
35
+ 'onwarn',
36
+ 'preprocess',
37
+ 'hot',
38
+ 'ignorePluginPreprocessors',
39
+ 'disableDependencyReinclusion',
40
+ 'experimental'
41
+ ]);
42
+
43
+ export function validateInlineOptions(inlineOptions?: Partial<Options>) {
44
+ const invalidKeys = Object.keys(inlineOptions || {}).filter((key) => !knownOptions.has(key));
45
+ if (invalidKeys.length) {
46
+ log.warn(`invalid plugin options "${invalidKeys.join(', ')}" in config`, inlineOptions);
47
+ }
48
+ }
49
+
50
+ // used in config phase, merges the default options, svelte config, and inline options
51
+ export async function preResolveOptions(
52
+ inlineOptions: Partial<Options> = {},
53
+ viteUserConfig: UserConfig,
54
+ viteEnv: ConfigEnv
55
+ ): Promise<PreResolvedOptions> {
56
+ const viteConfigWithResolvedRoot: UserConfig = {
57
+ ...viteUserConfig,
58
+ root: resolveViteRoot(viteUserConfig)
59
+ };
60
+ const defaultOptions: Partial<Options> = {
61
+ extensions: ['.svelte'],
62
+ emitCss: true,
63
+ compilerOptions: {
64
+ format: 'esm'
65
+ }
66
+ };
67
+ const svelteConfig = await loadSvelteConfig(viteConfigWithResolvedRoot, inlineOptions);
68
+ const merged = {
69
+ ...defaultOptions,
70
+ ...svelteConfig,
71
+ ...inlineOptions,
72
+ compilerOptions: {
73
+ ...defaultOptions?.compilerOptions,
74
+ ...svelteConfig?.compilerOptions,
75
+ ...inlineOptions?.compilerOptions
76
+ },
77
+ experimental: {
78
+ ...defaultOptions?.experimental,
79
+ ...svelteConfig?.experimental,
80
+ ...inlineOptions?.experimental
81
+ },
82
+ // extras
83
+ root: viteConfigWithResolvedRoot.root!,
84
+ isBuild: viteEnv.command === 'build',
85
+ isServe: viteEnv.command === 'serve'
86
+ };
87
+ // configFile of svelteConfig contains the absolute path it was loaded from,
88
+ // prefer it over the possibly relative inline path
89
+ if (svelteConfig?.configFile) {
90
+ merged.configFile = svelteConfig.configFile;
91
+ }
92
+ return merged;
93
+ }
94
+
95
+ // used in configResolved phase, merges a contextual default config, pre-resolved options, and some preprocessors.
96
+ // also validates the final config.
97
+ export function resolveOptions(
98
+ preResolveOptions: PreResolvedOptions,
99
+ viteConfig: ResolvedConfig
100
+ ): ResolvedOptions {
101
+ const defaultOptions: Partial<Options> = {
102
+ hot: viteConfig.isProduction ? false : { injectCss: !preResolveOptions.emitCss },
103
+ compilerOptions: {
104
+ css: !preResolveOptions.emitCss,
105
+ dev: !viteConfig.isProduction
106
+ }
107
+ };
108
+ const merged: ResolvedOptions = {
109
+ ...defaultOptions,
110
+ ...preResolveOptions,
111
+ compilerOptions: {
112
+ ...defaultOptions.compilerOptions,
113
+ ...preResolveOptions.compilerOptions
114
+ },
115
+ isProduction: viteConfig.isProduction
116
+ };
117
+ addExtraPreprocessors(merged, viteConfig);
118
+ enforceOptionsForHmr(merged);
119
+ enforceOptionsForProduction(merged);
120
+ return merged;
121
+ }
122
+
123
+ function enforceOptionsForHmr(options: ResolvedOptions) {
124
+ if (options.hot) {
125
+ if (!options.compilerOptions.dev) {
126
+ log.warn('hmr is enabled but compilerOptions.dev is false, forcing it to true');
127
+ options.compilerOptions.dev = true;
128
+ }
129
+ if (options.emitCss) {
130
+ if (options.hot !== true && options.hot.injectCss) {
131
+ log.warn('hmr and emitCss are enabled but hot.injectCss is true, forcing it to false');
132
+ options.hot.injectCss = false;
133
+ }
134
+ if (options.compilerOptions.css) {
135
+ log.warn(
136
+ 'hmr and emitCss are enabled but compilerOptions.css is true, forcing it to false'
137
+ );
138
+ options.compilerOptions.css = false;
139
+ }
140
+ } else {
141
+ if (options.hot === true || !options.hot.injectCss) {
142
+ log.warn(
143
+ 'hmr with emitCss disabled requires option hot.injectCss to be enabled, forcing it to true'
144
+ );
145
+ if (options.hot === true) {
146
+ options.hot = { injectCss: true };
147
+ } else {
148
+ options.hot.injectCss = true;
149
+ }
150
+ }
151
+ if (!options.compilerOptions.css) {
152
+ log.warn(
153
+ 'hmr with emitCss disabled requires compilerOptions.css to be enabled, forcing it to true'
154
+ );
155
+ options.compilerOptions.css = true;
156
+ }
157
+ }
158
+ }
159
+ }
160
+
161
+ function enforceOptionsForProduction(options: ResolvedOptions) {
162
+ if (options.isProduction) {
163
+ if (options.hot) {
164
+ log.warn('options.hot is enabled but does not work on production build, forcing it to false');
165
+ options.hot = false;
166
+ }
167
+ if (options.compilerOptions.dev) {
168
+ log.warn(
169
+ 'you are building for production but compilerOptions.dev is true, forcing it to false'
170
+ );
171
+ options.compilerOptions.dev = false;
172
+ }
173
+ }
174
+ }
175
+
176
+ // vite passes unresolved `root`option to config hook but we need the resolved value, so do it here
177
+ // https://github.com/sveltejs/vite-plugin-svelte/issues/113
178
+ // https://github.com/vitejs/vite/blob/43c957de8a99bb326afd732c962f42127b0a4d1e/packages/vite/src/node/config.ts#L293
179
+ function resolveViteRoot(viteConfig: UserConfig): string | undefined {
180
+ return normalizePath(viteConfig.root ? path.resolve(viteConfig.root) : process.cwd());
181
+ }
182
+
183
+ export function buildExtraViteConfig(
184
+ options: PreResolvedOptions,
185
+ config: UserConfig,
186
+ configEnv: ConfigEnv
187
+ ): Partial<UserConfig> {
188
+ // extra handling for svelte dependencies in the project
189
+ const svelteDeps = findRootSvelteDependencies(options.root);
190
+ const extraViteConfig: Partial<UserConfig> = {
191
+ resolve: {
192
+ mainFields: [...SVELTE_RESOLVE_MAIN_FIELDS],
193
+ dedupe: [...SVELTE_IMPORTS, ...SVELTE_HMR_IMPORTS]
194
+ }
195
+ // this option is still awaiting a PR in vite to be supported
196
+ // see https://github.com/sveltejs/vite-plugin-svelte/issues/60
197
+ // @ts-ignore
198
+ // knownJsSrcExtensions: options.extensions
199
+ };
200
+
201
+ if (configEnv.command === 'serve') {
202
+ extraViteConfig.optimizeDeps = buildOptimizeDepsForSvelte(
203
+ svelteDeps,
204
+ options,
205
+ config.optimizeDeps
206
+ );
207
+ }
208
+
209
+ // @ts-ignore
210
+ extraViteConfig.ssr = buildSSROptionsForSvelte(svelteDeps, options, config);
211
+
212
+ return extraViteConfig;
213
+ }
214
+
215
+ function buildOptimizeDepsForSvelte(
216
+ svelteDeps: SvelteDependency[],
217
+ options: PreResolvedOptions,
218
+ optimizeDeps?: DepOptimizationOptions
219
+ ): DepOptimizationOptions {
220
+ // include svelte imports for optimization unless explicitly excluded
221
+ const include: string[] = [];
222
+ const exclude: string[] = ['svelte-hmr'];
223
+ const isIncluded = (dep: string) => include.includes(dep) || optimizeDeps?.include?.includes(dep);
224
+ const isExcluded = (dep: string) => {
225
+ return (
226
+ exclude.includes(dep) ||
227
+ // vite optimizeDeps.exclude works for subpackages too
228
+ // see https://github.com/vitejs/vite/blob/c87763c1418d1ba876eae13d139eba83ac6f28b2/packages/vite/src/node/optimizer/scan.ts#L293
229
+ optimizeDeps?.exclude?.some((id: string) => dep === id || id.startsWith(`${dep}/`))
230
+ );
231
+ };
232
+ if (!isExcluded('svelte')) {
233
+ const svelteImportsToInclude = SVELTE_IMPORTS.filter((x) => x !== 'svelte/ssr'); // not used on clientside
234
+ log.debug(
235
+ `adding bare svelte packages to optimizeDeps.include: ${svelteImportsToInclude.join(', ')} `
236
+ );
237
+ include.push(...svelteImportsToInclude.filter((x) => !isIncluded(x)));
238
+ } else {
239
+ log.debug('"svelte" is excluded in optimizeDeps.exclude, skipped adding it to include.');
240
+ }
241
+
242
+ // If we prebundle svelte libraries, we can skip the whole prebundling dance below
243
+ if (options.experimental.prebundleSvelteLibraries) {
244
+ return {
245
+ include,
246
+ exclude,
247
+ esbuildOptions: {
248
+ plugins: [{ name: facadeEsbuildSveltePluginName, setup: () => {} }]
249
+ }
250
+ };
251
+ }
252
+
253
+ // only svelte component libraries needs to be processed for optimizeDeps, js libraries work fine
254
+ svelteDeps = svelteDeps.filter((dep) => dep.type === 'component-library');
255
+
256
+ const svelteDepsToExclude = Array.from(new Set(svelteDeps.map((dep) => dep.name))).filter(
257
+ (dep) => !isIncluded(dep)
258
+ );
259
+ log.debug(`automatically excluding found svelte dependencies: ${svelteDepsToExclude.join(', ')}`);
260
+ exclude.push(...svelteDepsToExclude.filter((x) => !isExcluded(x)));
261
+
262
+ if (options.disableDependencyReinclusion !== true) {
263
+ const disabledReinclusions = options.disableDependencyReinclusion || [];
264
+ if (disabledReinclusions.length > 0) {
265
+ log.debug(`not reincluding transitive dependencies of`, disabledReinclusions);
266
+ }
267
+ const transitiveDepsToInclude = svelteDeps
268
+ .filter((dep) => !disabledReinclusions.includes(dep.name) && isExcluded(dep.name))
269
+ .flatMap((dep) => {
270
+ const localRequire = createRequire(`${dep.dir}/package.json`);
271
+ return Object.keys(dep.pkg.dependencies || {})
272
+ .filter((depOfDep) => !isExcluded(depOfDep) && needsOptimization(depOfDep, localRequire))
273
+ .map((depOfDep) => dep.path.concat(dep.name, depOfDep).join(' > '));
274
+ });
275
+ log.debug(
276
+ `reincluding transitive dependencies of excluded svelte dependencies`,
277
+ transitiveDepsToInclude
278
+ );
279
+ include.push(...transitiveDepsToInclude);
280
+ }
281
+
282
+ return { include, exclude };
283
+ }
284
+
285
+ function buildSSROptionsForSvelte(
286
+ svelteDeps: SvelteDependency[],
287
+ options: ResolvedOptions,
288
+ config: UserConfig
289
+ ): any {
290
+ const noExternal: string[] = [];
291
+
292
+ // add svelte to ssr.noExternal unless it is present in ssr.external
293
+ // so we can resolve it with svelte/ssr
294
+ if (options.isBuild && config.build?.ssr) {
295
+ // @ts-ignore
296
+ if (!config.ssr?.external?.includes('svelte')) {
297
+ noExternal.push('svelte');
298
+ }
299
+ } else {
300
+ // for non-ssr build, we exclude svelte js library deps to make development faster
301
+ // and also because vite doesn't handle them properly.
302
+ // see https://github.com/sveltejs/vite-plugin-svelte/issues/168
303
+ // see https://github.com/vitejs/vite/issues/2579
304
+ svelteDeps = svelteDeps.filter((dep) => dep.type === 'component-library');
305
+ }
306
+
307
+ // add svelte dependencies to ssr.noExternal unless present in ssr.external or optimizeDeps.include
308
+ noExternal.push(
309
+ ...Array.from(new Set(svelteDeps.map((s) => s.name))).filter((x) => {
310
+ // @ts-ignore
311
+ return !config.ssr?.external?.includes(x) && !config.optimizeDeps?.include?.includes(x);
312
+ })
313
+ );
314
+ return {
315
+ noExternal
316
+ };
317
+ }
318
+
319
+ export function patchResolvedViteConfig(viteConfig: ResolvedConfig, options: ResolvedOptions) {
320
+ const facadeEsbuildSveltePlugin = viteConfig.optimizeDeps.esbuildOptions?.plugins?.find(
321
+ (plugin) => plugin.name === facadeEsbuildSveltePluginName
322
+ );
323
+ if (facadeEsbuildSveltePlugin) {
324
+ Object.assign(facadeEsbuildSveltePlugin, esbuildSveltePlugin(options));
325
+ }
326
+ }
327
+ export interface Options {
328
+ /**
329
+ * Path to a svelte config file, either absolute or relative to Vite root
330
+ *
331
+ * @see https://vitejs.dev/config/#root
332
+ */
333
+ configFile?: string;
334
+
335
+ /**
336
+ * A `picomatch` pattern, or array of patterns, which specifies the files the plugin should
337
+ * operate on. By default, all svelte files are included.
338
+ *
339
+ * @see https://github.com/micromatch/picomatch
340
+ */
341
+ include?: Arrayable<string>;
342
+
343
+ /**
344
+ * A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the
345
+ * plugin. By default, no files are ignored.
346
+ *
347
+ * @see https://github.com/micromatch/picomatch
348
+ */
349
+ exclude?: Arrayable<string>;
350
+
351
+ /**
352
+ * A list of file extensions to be compiled by Svelte
353
+ *
354
+ * @default ['.svelte']
355
+ */
356
+ extensions?: string[];
357
+
358
+ /**
359
+ * An array of preprocessors to transform the Svelte source code before compilation
360
+ *
361
+ * @see https://svelte.dev/docs#svelte_preprocess
362
+ */
363
+ preprocess?: Arrayable<PreprocessorGroup>;
364
+
365
+ /**
366
+ * Emit Svelte styles as virtual CSS files for Vite and other plugins to process
367
+ *
368
+ * @default true
369
+ */
370
+ emitCss?: boolean;
371
+
372
+ /**
373
+ * The options to be passed to the Svelte compiler
374
+ *
375
+ * @see https://svelte.dev/docs#svelte_compile
376
+ */
377
+ compilerOptions?: CompileOptions;
378
+
379
+ /**
380
+ * Handles warning emitted from the Svelte compiler
381
+ */
382
+ onwarn?: (warning: Warning, defaultHandler?: (warning: Warning) => void) => void;
383
+
384
+ /**
385
+ * Enable or disable Hot Module Replacement.
386
+ *
387
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
388
+ *
389
+ * DO NOT CUSTOMIZE SVELTE-HMR OPTIONS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING
390
+ *
391
+ * YOU HAVE BEEN WARNED
392
+ *
393
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
394
+ *
395
+ * Set an object to pass custom options to svelte-hmr
396
+ *
397
+ * @see https://github.com/rixo/svelte-hmr#options
398
+ * @default true for development, always false for production
399
+ */
400
+ hot?: boolean | { injectCss?: boolean; [key: string]: any };
401
+
402
+ /**
403
+ * Some Vite plugins can contribute additional preprocessors by defining `api.sveltePreprocess`.
404
+ * If you don't want to use them, set this to true to ignore them all or use an array of strings
405
+ * with plugin names to specify which.
406
+ *
407
+ * @default false
408
+ */
409
+ ignorePluginPreprocessors?: boolean | string[];
410
+
411
+ /**
412
+ * vite-plugin-svelte automatically handles excluding svelte libraries and reinclusion of their dependencies
413
+ * in vite.optimizeDeps.
414
+ *
415
+ * `disableDependencyReinclusion: true` disables all reinclusions
416
+ * `disableDependencyReinclusion: ['foo','bar']` disables reinclusions for dependencies of foo and bar
417
+ *
418
+ * This should be used for hybrid packages that contain both node and browser dependencies, eg Routify
419
+ *
420
+ * @default false
421
+ */
422
+ disableDependencyReinclusion?: boolean | string[];
423
+
424
+ /**
425
+ * These options are considered experimental and breaking changes to them can occur in any release
426
+ */
427
+ experimental?: ExperimentalOptions;
428
+ }
429
+
430
+ /**
431
+ * These options are considered experimental and breaking changes to them can occur in any release
432
+ */
433
+ export interface ExperimentalOptions {
434
+ /**
435
+ * Use extra preprocessors that delegate style and TypeScript preprocessing to native Vite plugins
436
+ *
437
+ * Do not use together with `svelte-preprocess`!
438
+ *
439
+ * @default false
440
+ */
441
+ useVitePreprocess?: boolean;
442
+
443
+ /**
444
+ * Force Vite to pre-bundle Svelte libraries
445
+ *
446
+ * @default false
447
+ */
448
+ prebundleSvelteLibraries?: boolean;
449
+
450
+ /**
451
+ * If a preprocessor does not provide a sourcemap, a best-effort fallback sourcemap will be provided.
452
+ * This option requires `diff-match-patch` to be installed as a peer dependency.
453
+ *
454
+ * @see https://github.com/google/diff-match-patch
455
+ * @default false
456
+ */
457
+ generateMissingPreprocessorSourcemaps?: boolean;
458
+
459
+ /**
460
+ * A function to update `compilerOptions` before compilation
461
+ *
462
+ * `data.filename` - The file to be compiled
463
+ * `data.code` - The preprocessed Svelte code
464
+ * `data.compileOptions` - The current compiler options
465
+ *
466
+ * To change part of the compiler options, return an object with the changes you need.
467
+ *
468
+ * @example
469
+ * ```
470
+ * ({ filename, compileOptions }) => {
471
+ * // Dynamically set hydration per Svelte file
472
+ * if (compileWithHydratable(filename) && !compileOptions.hydratable) {
473
+ * return { hydratable: true };
474
+ * }
475
+ * }
476
+ * ```
477
+ */
478
+ dynamicCompileOptions?: (data: {
479
+ filename: string;
480
+ code: string;
481
+ compileOptions: Partial<CompileOptions>;
482
+ }) => Promise<Partial<CompileOptions> | void> | Partial<CompileOptions> | void;
483
+ }
484
+
485
+ export interface PreResolvedOptions extends Options {
486
+ // these options are non-nullable after resolve
487
+ compilerOptions: CompileOptions;
488
+ experimental: ExperimentalOptions;
489
+ // extra options
490
+ root: string;
491
+ isBuild: boolean;
492
+ isServe: boolean;
493
+ }
494
+
495
+ export interface ResolvedOptions extends PreResolvedOptions {
496
+ isProduction: boolean;
497
+ server?: ViteDevServer;
498
+ }
499
+
500
+ export type {
501
+ CompileOptions,
502
+ Processed,
503
+ MarkupPreprocessor,
504
+ Preprocessor,
505
+ PreprocessorGroup,
506
+ Warning
507
+ };
508
+
509
+ export type ModuleFormat = NonNullable<CompileOptions['format']>;
510
+
511
+ export type CssHashGetter = NonNullable<CompileOptions['cssHash']>;
512
+
513
+ export type Arrayable<T> = T | T[];