@sveltejs/vite-plugin-svelte 2.4.6 → 2.5.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/vite-plugin-svelte",
3
- "version": "2.4.6",
3
+ "version": "2.5.1",
4
4
  "license": "MIT",
5
5
  "author": "dominikg",
6
6
  "files": [
@@ -43,7 +43,7 @@
43
43
  "vitefu": "^0.2.4"
44
44
  },
45
45
  "peerDependencies": {
46
- "svelte": "^3.54.0 || ^4.0.0",
46
+ "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0",
47
47
  "vite": "^4.0.0"
48
48
  },
49
49
  "devDependencies": {
package/src/index.d.ts CHANGED
@@ -170,6 +170,16 @@ interface ExperimentalOptions {
170
170
  * @default false
171
171
  */
172
172
  disableSvelteResolveWarnings?: boolean;
173
+ /**
174
+ * Options for compiling Svelte JS/TS modules
175
+ */
176
+ compileModule?: CompileModuleOptions;
177
+ }
178
+
179
+ interface CompileModuleOptions {
180
+ extensions?: string[];
181
+ include?: Arrayable<string>;
182
+ exclude?: Arrayable<string>;
173
183
  }
174
184
 
175
185
  type ModuleFormat = NonNullable<'esm'>;
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import fs from 'node:fs';
2
2
  import { version as viteVersion } from 'vite';
3
+ import * as svelteCompiler from 'svelte/compiler';
3
4
 
4
5
  import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector';
5
6
 
@@ -7,7 +8,7 @@ import { isDepExcluded } from 'vitefu';
7
8
  import { handleHotUpdate } from './handle-hot-update.js';
8
9
  import { log, logCompilerWarnings } from './utils/log.js';
9
10
  import { createCompileSvelte } from './utils/compile.js';
10
- import { buildIdParser } from './utils/id.js';
11
+ import { buildIdParser, buildModuleIdParser } from './utils/id.js';
11
12
  import {
12
13
  buildExtraViteConfig,
13
14
  validateInlineOptions,
@@ -24,7 +25,7 @@ import { saveSvelteMetadata } from './utils/optimizer.js';
24
25
  import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache.js';
25
26
  import { loadRaw } from './utils/load-raw.js';
26
27
  import { FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE } from './utils/constants.js';
27
- import { isSvelte3 } from './utils/svelte-version.js';
28
+ import { isSvelte3, isSvelte5 } from './utils/svelte-version.js';
28
29
 
29
30
  const isVite4_0 = viteVersion.startsWith('4.0');
30
31
 
@@ -38,6 +39,8 @@ export function svelte(inlineOptions) {
38
39
  // updated in configResolved hook
39
40
  /** @type {import('./types/id.d.ts').IdParser} */
40
41
  let requestParser;
42
+ /** @type {import('./types/id.d.ts').ModuleIdParser} */
43
+ let moduleRequestParser;
41
44
  /** @type {import('./types/options.d.ts').ResolvedOptions} */
42
45
  let options;
43
46
  /** @type {import('vite').ResolvedConfig} */
@@ -268,9 +271,45 @@ export function svelte(inlineOptions) {
268
271
  );
269
272
  }
270
273
  }
271
- },
272
- svelteInspector()
274
+ }
273
275
  ];
276
+
277
+ if (!isSvelte5) {
278
+ plugins.push(svelteInspector()); // TODO reenable once svelte5 has support
279
+ }
280
+ if (isSvelte5) {
281
+ log.warn(
282
+ 'svelte 5 support in v-p-s is experimental, breaking changes can occur in any release until this notice is removed'
283
+ );
284
+ log.warn('svelte 5 does not support svelte-inspector yet, disabling it');
285
+ // TODO move to separate file
286
+ plugins.push({
287
+ name: 'vite-plugin-svelte-module',
288
+ enforce: 'post',
289
+ async configResolved() {
290
+ moduleRequestParser = buildModuleIdParser(options);
291
+ },
292
+ async transform(code, id, opts) {
293
+ const ssr = !!opts?.ssr;
294
+ const moduleRequest = moduleRequestParser(id, ssr);
295
+ if (!moduleRequest) {
296
+ return;
297
+ }
298
+ try {
299
+ // @ts-ignore doesn't exist in Svelte 4
300
+ const compileResult = await svelteCompiler.compileModule(code, {
301
+ generate: isSvelte5 ? (ssr ? 'server' : 'client') : ssr ? 'ssr' : 'dom',
302
+ filename: moduleRequest.filename
303
+ });
304
+ logCompilerWarnings(moduleRequest, compileResult.warnings, options);
305
+ return compileResult.js;
306
+ } catch (e) {
307
+ throw toRollupError(e, options);
308
+ }
309
+ }
310
+ });
311
+ }
312
+
274
313
  return plugins;
275
314
  }
276
315
 
package/src/types/id.d.ts CHANGED
@@ -28,4 +28,19 @@ export interface SvelteRequest {
28
28
  raw: boolean;
29
29
  }
30
30
 
31
+ export interface SvelteModuleRequest {
32
+ id: string;
33
+ filename: string;
34
+ normalizedFilename: string;
35
+ query: RequestQuery;
36
+ timestamp: number;
37
+ ssr: boolean;
38
+ }
39
+
31
40
  export type IdParser = (id: string, ssr: boolean, timestamp?: number) => SvelteRequest | undefined;
41
+
42
+ export type ModuleIdParser = (
43
+ id: string,
44
+ ssr: boolean,
45
+ timestamp?: number
46
+ ) => SvelteModuleRequest | undefined;
@@ -1,4 +1,4 @@
1
- import { compile, preprocess, walk } from 'svelte/compiler';
1
+ import * as svelte from 'svelte/compiler';
2
2
  // @ts-ignore
3
3
  import { createMakeHot } from 'svelte-hmr';
4
4
  import { safeBase64Hash } from './hash.js';
@@ -9,7 +9,7 @@ import { mapToRelative } from './sourcemaps.js';
9
9
 
10
10
  const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/;
11
11
 
12
- import { isSvelte3 } from './svelte-version.js';
12
+ import { isSvelte3, isSvelte5 } from './svelte-version.js';
13
13
 
14
14
  /**
15
15
  * @param {Function} [makeHot]
@@ -53,7 +53,8 @@ export const _createCompileSvelte = (makeHot) => {
53
53
  const compileOptions = {
54
54
  ...options.compilerOptions,
55
55
  filename,
56
- generate: ssr ? 'ssr' : 'dom'
56
+ // @ts-expect-error generate type is different for svelte5
57
+ generate: isSvelte5 ? (ssr ? 'server' : 'client') : ssr ? 'ssr' : 'dom'
57
58
  };
58
59
  if (isSvelte3) {
59
60
  // @ts-ignore
@@ -86,7 +87,7 @@ export const _createCompileSvelte = (makeHot) => {
86
87
  }
87
88
  if (preprocessors) {
88
89
  try {
89
- preprocessed = await preprocess(code, preprocessors, { filename }); // full filename here so postcss works
90
+ preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works
90
91
  } catch (e) {
91
92
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
92
93
  throw e;
@@ -123,7 +124,7 @@ export const _createCompileSvelte = (makeHot) => {
123
124
  : compileOptions;
124
125
 
125
126
  const endStat = stats?.start(filename);
126
- const compiled = compile(finalCode, finalCompileOptions);
127
+ const compiled = svelte.compile(finalCode, finalCompileOptions);
127
128
 
128
129
  if (isSvelte3) {
129
130
  // prevent dangling pure comments
@@ -187,7 +188,8 @@ function buildMakeHot(options) {
187
188
  // @ts-ignore
188
189
  const adapter = options?.hot?.adapter;
189
190
  return createMakeHot({
190
- walk,
191
+ // TODO Svelte 5 doesn't expose walk anymore. If we decide to make v-p-s 2 work with Svelte 5 HMR, we need to import walk from estree-walker
192
+ walk: svelte.walk,
191
193
  hotApi,
192
194
  adapter,
193
195
  hotOptions: { noOverlay: true, .../** @type {object} */ (options.hot) }
@@ -1,8 +1,8 @@
1
1
  import { readFileSync } from 'node:fs';
2
- import { compile, preprocess } from 'svelte/compiler';
2
+ import * as svelte from 'svelte/compiler';
3
3
  import { log } from './log.js';
4
4
  import { toESBuildError } from './error.js';
5
- import { isSvelte3 } from './svelte-version.js';
5
+ import { isSvelte3, isSvelte5 } from './svelte-version.js';
6
6
 
7
7
  /**
8
8
  * @typedef {NonNullable<import('vite').DepOptimizationOptions['esbuildOptions']>} EsbuildOptions
@@ -11,6 +11,8 @@ import { isSvelte3 } from './svelte-version.js';
11
11
 
12
12
  export const facadeEsbuildSveltePluginName = 'vite-plugin-svelte:facade';
13
13
 
14
+ const svelteModuleExtension = '.svelte.js';
15
+
14
16
  /**
15
17
  * @param {import('../types/options.d.ts').ResolvedOptions} options
16
18
  * @returns {EsbuildPlugin}
@@ -24,6 +26,9 @@ export function esbuildSveltePlugin(options) {
24
26
  if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
25
27
 
26
28
  const svelteExtensions = (options.extensions ?? ['.svelte']).map((ext) => ext.slice(1));
29
+ if (isSvelte5) {
30
+ svelteExtensions.push(svelteModuleExtension.slice(1));
31
+ }
27
32
  const svelteFilter = new RegExp('\\.(' + svelteExtensions.join('|') + ')(\\?.*)?$');
28
33
  /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
29
34
  let statsCollection;
@@ -55,6 +60,22 @@ export function esbuildSveltePlugin(options) {
55
60
  * @returns {Promise<string>}
56
61
  */
57
62
  async function compileSvelte(options, { filename, code }, statsCollection) {
63
+ if (isSvelte5 && filename.endsWith(svelteModuleExtension)) {
64
+ const endStat = statsCollection?.start(filename);
65
+ // @ts-ignore doesn't exist in Svelte 4
66
+ const compiled = svelte.compileModule(code, {
67
+ filename,
68
+ generate: isSvelte5 ? 'client' : 'dom',
69
+ runes: true
70
+ });
71
+ if (endStat) {
72
+ endStat();
73
+ }
74
+ return compiled.js.map
75
+ ? compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl()
76
+ : compiled.js.code;
77
+ }
78
+
58
79
  let css = options.compilerOptions.css;
59
80
  if (css !== 'none') {
60
81
  // TODO ideally we'd be able to externalize prebundled styles too, but for now always put them in the js
@@ -65,7 +86,8 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
65
86
  ...options.compilerOptions,
66
87
  css,
67
88
  filename,
68
- generate: 'dom'
89
+ // @ts-expect-error generate type is different for svelte5
90
+ generate: isSvelte5 ? 'client' : 'dom'
69
91
  };
70
92
  if (isSvelte3) {
71
93
  // @ts-ignore
@@ -75,7 +97,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
75
97
 
76
98
  if (options.preprocess) {
77
99
  try {
78
- preprocessed = await preprocess(code, options.preprocess, { filename });
100
+ preprocessed = await svelte.preprocess(code, options.preprocess, { filename });
79
101
  } catch (e) {
80
102
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
81
103
  throw e;
@@ -102,7 +124,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
102
124
  }
103
125
  : compileOptions;
104
126
  const endStat = statsCollection?.start(filename);
105
- const compiled = compile(finalCode, finalCompileOptions);
127
+ const compiled = svelte.compile(finalCode, finalCompileOptions);
106
128
  if (endStat) {
107
129
  endStat();
108
130
  }
package/src/utils/id.js CHANGED
@@ -184,3 +184,48 @@ export function buildIdParser(options) {
184
184
  }
185
185
  };
186
186
  }
187
+ /**
188
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
189
+ * @returns {import('../types/id.d.ts').ModuleIdParser}
190
+ */
191
+ export function buildModuleIdParser(options) {
192
+ const { include, exclude, extensions } = options?.experimental?.compileModule ?? {};
193
+ const root = options.root;
194
+ const normalizedRoot = normalizePath(root);
195
+ const filter = buildFilter(include, exclude, extensions ?? ['.svelte.js', '.svelte.ts']);
196
+ return (id, ssr, timestamp = Date.now()) => {
197
+ const { filename, rawQuery } = splitId(id);
198
+ if (filter(filename)) {
199
+ return parseToSvelteModuleRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr);
200
+ }
201
+ };
202
+ }
203
+
204
+ /**
205
+ * @param {string} id
206
+ * @param {string} filename
207
+ * @param {string} rawQuery
208
+ * @param {string} root
209
+ * @param {number} timestamp
210
+ * @param {boolean} ssr
211
+ * @returns {import('../types/id.d.ts').SvelteModuleRequest | undefined}
212
+ */
213
+ function parseToSvelteModuleRequest(id, filename, rawQuery, root, timestamp, ssr) {
214
+ const query = parseRequestQuery(rawQuery);
215
+
216
+ if (query.url || query.raw || query.direct) {
217
+ // skip requests with special vite tags
218
+ return;
219
+ }
220
+
221
+ const normalizedFilename = normalize(filename, root);
222
+
223
+ return {
224
+ id,
225
+ filename,
226
+ normalizedFilename,
227
+ query,
228
+ timestamp,
229
+ ssr
230
+ };
231
+ }
package/src/utils/log.js CHANGED
@@ -120,7 +120,7 @@ export const log = {
120
120
  };
121
121
 
122
122
  /**
123
- * @param {import('../types/id.d.ts').SvelteRequest} svelteRequest
123
+ * @param {import('../types/id.d.ts').SvelteRequest | import('../types/id.d.ts').SvelteModuleRequest} svelteRequest
124
124
  * @param {import('svelte/types/compiler/interfaces').Warning[]} warnings
125
125
  * @param {import('../types/options.d.ts').ResolvedOptions} options
126
126
  */
@@ -25,6 +25,7 @@ import {
25
25
  import { isCommonDepWithoutSvelteField } from './dependencies.js';
26
26
  import { VitePluginSvelteStats } from './vite-plugin-svelte-stats.js';
27
27
  import { VitePluginSvelteCache } from './vite-plugin-svelte-cache.js';
28
+ import { isSvelte5 } from './svelte-version.js';
28
29
 
29
30
  const allowedPluginOptions = new Set([
30
31
  'include',
@@ -227,6 +228,10 @@ export function resolveOptions(preResolveOptions, viteConfig, cache) {
227
228
  * @param {import('../types/options.d.ts').ResolvedOptions} options
228
229
  */
229
230
  function enforceOptionsForHmr(options) {
231
+ if (isSvelte5) {
232
+ log.warn('svelte 5 does not support hmr api yet, disabling it for now');
233
+ options.hot = false;
234
+ }
230
235
  if (options.hot) {
231
236
  if (!options.compilerOptions.dev) {
232
237
  log.warn('hmr is enabled but compilerOptions.dev is false, forcing it to true');
@@ -4,3 +4,8 @@ import { VERSION } from 'svelte/compiler';
4
4
  * @type {boolean}
5
5
  */
6
6
  export const isSvelte3 = VERSION.startsWith('3.');
7
+
8
+ /**
9
+ * @type {boolean}
10
+ */
11
+ export const isSvelte5 = VERSION.startsWith('5.');