@sveltejs/vite-plugin-svelte 2.4.6 → 2.5.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.
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.0",
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: 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';
@@ -86,7 +86,7 @@ export const _createCompileSvelte = (makeHot) => {
86
86
  }
87
87
  if (preprocessors) {
88
88
  try {
89
- preprocessed = await preprocess(code, preprocessors, { filename }); // full filename here so postcss works
89
+ preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works
90
90
  } catch (e) {
91
91
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
92
92
  throw e;
@@ -123,7 +123,7 @@ export const _createCompileSvelte = (makeHot) => {
123
123
  : compileOptions;
124
124
 
125
125
  const endStat = stats?.start(filename);
126
- const compiled = compile(finalCode, finalCompileOptions);
126
+ const compiled = svelte.compile(finalCode, finalCompileOptions);
127
127
 
128
128
  if (isSvelte3) {
129
129
  // prevent dangling pure comments
@@ -187,7 +187,8 @@ function buildMakeHot(options) {
187
187
  // @ts-ignore
188
188
  const adapter = options?.hot?.adapter;
189
189
  return createMakeHot({
190
- walk,
190
+ // 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
191
+ walk: svelte.walk,
191
192
  hotApi,
192
193
  adapter,
193
194
  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: '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
@@ -75,7 +96,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
75
96
 
76
97
  if (options.preprocess) {
77
98
  try {
78
- preprocessed = await preprocess(code, options.preprocess, { filename });
99
+ preprocessed = await svelte.preprocess(code, options.preprocess, { filename });
79
100
  } catch (e) {
80
101
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
81
102
  throw e;
@@ -102,7 +123,7 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
102
123
  }
103
124
  : compileOptions;
104
125
  const endStat = statsCollection?.start(filename);
105
- const compiled = compile(finalCode, finalCompileOptions);
126
+ const compiled = svelte.compile(finalCode, finalCompileOptions);
106
127
  if (endStat) {
107
128
  endStat();
108
129
  }
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.');