@sveltejs/vite-plugin-svelte 3.0.0-next.2 → 3.0.0-next.3

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": "3.0.0-next.2",
3
+ "version": "3.0.0-next.3",
4
4
  "license": "MIT",
5
5
  "author": "dominikg",
6
6
  "files": [
@@ -45,15 +45,15 @@
45
45
  "vitefu": "^0.2.5"
46
46
  },
47
47
  "peerDependencies": {
48
- "svelte": "^4.0.0",
48
+ "svelte": "^4.0.0 || ^5.0.0-next.0",
49
49
  "vite": "^5.0.0-beta.1 || ^5.0.0"
50
50
  },
51
51
  "devDependencies": {
52
- "@types/debug": "^4.1.10",
52
+ "@types/debug": "^4.1.12",
53
53
  "esbuild": "^0.19.5",
54
54
  "sass": "^1.69.5",
55
- "svelte": "^4.2.2",
56
- "vite": "^5.0.0-beta.12"
55
+ "svelte": "^4.2.3",
56
+ "vite": "^5.0.0-beta.19"
57
57
  },
58
58
  "scripts": {
59
59
  "check:publint": "publint --strict",
@@ -7,7 +7,7 @@ import { toRollupError } from './utils/error.js';
7
7
  * @param {Function} compileSvelte
8
8
  * @param {import('vite').HmrContext} ctx
9
9
  * @param {import('./types/id.d.ts').SvelteRequest} svelteRequest
10
- * @param {import('./utils/vite-plugin-svelte-cache').VitePluginSvelteCache} cache
10
+ * @param {import('./utils/vite-plugin-svelte-cache.js').VitePluginSvelteCache} cache
11
11
  * @param {import('./types/options.d.ts').ResolvedOptions} options
12
12
  * @returns {Promise<import('vite').ModuleNode[] | void>}
13
13
  */
package/src/index.js CHANGED
@@ -3,9 +3,9 @@ import fs from 'node:fs';
3
3
  import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector';
4
4
 
5
5
  import { handleHotUpdate } from './handle-hot-update.js';
6
- import { log, logCompilerWarnings } from './utils/log.js';
6
+ import { log, logCompilerWarnings, logSvelte5Warning } from './utils/log.js';
7
7
  import { createCompileSvelte } from './utils/compile.js';
8
- import { buildIdParser } from './utils/id.js';
8
+ import { buildIdParser, buildModuleIdParser } from './utils/id.js';
9
9
  import {
10
10
  buildExtraViteConfig,
11
11
  validateInlineOptions,
@@ -19,6 +19,8 @@ import { toRollupError } from './utils/error.js';
19
19
  import { saveSvelteMetadata } from './utils/optimizer.js';
20
20
  import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache.js';
21
21
  import { loadRaw } from './utils/load-raw.js';
22
+ import { isSvelte5 } from './utils/svelte-version.js';
23
+ import * as svelteCompiler from 'svelte/compiler';
22
24
 
23
25
  /**
24
26
  * @param {Partial<import('./public.d.ts').Options>} [inlineOptions]
@@ -33,6 +35,8 @@ export function svelte(inlineOptions) {
33
35
  // updated in configResolved hook
34
36
  /** @type {import('./types/id.d.ts').IdParser} */
35
37
  let requestParser;
38
+ /** @type {import('./types/id.d.ts').ModuleIdParser} */
39
+ let moduleRequestParser;
36
40
  /** @type {import('./types/options.d.ts').ResolvedOptions} */
37
41
  let options;
38
42
  /** @type {import('vite').ResolvedConfig} */
@@ -185,9 +189,41 @@ export function svelte(inlineOptions) {
185
189
  async buildEnd() {
186
190
  await options.stats?.finishAll();
187
191
  }
188
- },
189
- svelteInspector()
192
+ }
190
193
  ];
194
+ if (isSvelte5) {
195
+ logSvelte5Warning();
196
+ // TODO move to separate file
197
+ plugins.push({
198
+ name: 'vite-plugin-svelte-module',
199
+ enforce: 'post',
200
+ async configResolved() {
201
+ moduleRequestParser = buildModuleIdParser(options);
202
+ },
203
+ async transform(code, id, opts) {
204
+ const ssr = !!opts?.ssr;
205
+ const moduleRequest = moduleRequestParser(id, ssr);
206
+ if (!moduleRequest) {
207
+ return;
208
+ }
209
+ try {
210
+ // @ts-expect-error compileModule does not exist in svelte4
211
+ const compileResult = await svelteCompiler.compileModule(code, {
212
+ generate: ssr ? 'server' : 'client',
213
+ filename: moduleRequest.filename
214
+ });
215
+ logCompilerWarnings(moduleRequest, compileResult.warnings, options);
216
+ return compileResult.js;
217
+ } catch (e) {
218
+ throw toRollupError(e, options);
219
+ }
220
+ }
221
+ });
222
+ }
223
+ if (!isSvelte5) {
224
+ // TODO reenable once svelte5 has support and update utils/log.js#logSvelte5Warning
225
+ plugins.push(svelteInspector());
226
+ }
191
227
  return plugins;
192
228
  }
193
229
 
package/src/public.d.ts CHANGED
@@ -174,6 +174,14 @@ interface ExperimentalOptions {
174
174
  * @default false
175
175
  */
176
176
  disableSvelteResolveWarnings?: boolean;
177
+
178
+ compileModule?: CompileModuleOptions;
179
+ }
180
+
181
+ interface CompileModuleOptions {
182
+ extensions?: string[];
183
+ include?: Arrayable<string>;
184
+ exclude?: Arrayable<string>;
177
185
  }
178
186
 
179
187
  type Arrayable<T> = T | T[];
package/src/types/id.d.ts CHANGED
@@ -28,4 +28,18 @@ 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
+ export type ModuleIdParser = (
42
+ id: string,
43
+ ssr: boolean,
44
+ timestamp?: number
45
+ ) => 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';
@@ -10,6 +10,7 @@ import {
10
10
  } from './preprocess.js';
11
11
  import { mapToRelative } from './sourcemaps.js';
12
12
  import { enhanceCompileError } from './error.js';
13
+ import { isSvelte5 } from './svelte-version.js';
13
14
 
14
15
  const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/;
15
16
 
@@ -58,7 +59,8 @@ export const _createCompileSvelte = (makeHot) => {
58
59
  const compileOptions = {
59
60
  ...options.compilerOptions,
60
61
  filename,
61
- generate: ssr ? 'ssr' : 'dom'
62
+ // @ts-expect-error svelte5 uses server/client, svelte4 uses ssr/dom
63
+ generate: isSvelte5 ? (ssr ? 'server' : 'client') : ssr ? 'ssr' : 'dom'
62
64
  };
63
65
 
64
66
  if (options.hot && options.emitCss) {
@@ -87,7 +89,7 @@ export const _createCompileSvelte = (makeHot) => {
87
89
  }
88
90
  if (preprocessors) {
89
91
  try {
90
- preprocessed = await preprocess(code, preprocessors, { filename }); // full filename here so postcss works
92
+ preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works
91
93
  } catch (e) {
92
94
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
93
95
  throw e;
@@ -138,7 +140,7 @@ export const _createCompileSvelte = (makeHot) => {
138
140
  /** @type {import('svelte/types/compiler/interfaces').CompileResult} */
139
141
  let compiled;
140
142
  try {
141
- compiled = compile(finalCode, finalCompileOptions);
143
+ compiled = svelte.compile(finalCode, finalCompileOptions);
142
144
  } catch (e) {
143
145
  enhanceCompileError(e, code, preprocessors);
144
146
  throw e;
@@ -203,7 +205,7 @@ function buildMakeHot(options) {
203
205
  // @ts-ignore
204
206
  const adapter = options?.hot?.adapter;
205
207
  return createMakeHot({
206
- walk,
208
+ walk: svelte.walk,
207
209
  hotApi,
208
210
  adapter,
209
211
  hotOptions: { noOverlay: true, .../** @type {object} */ (options.hot) }
@@ -1,3 +1,5 @@
1
+ import { isSvelte5 } from './svelte-version.js';
2
+
1
3
  export const VITE_RESOLVE_MAIN_FIELDS = ['module', 'jsnext:main', 'jsnext'];
2
4
 
3
5
  export const SVELTE_RESOLVE_MAIN_FIELDS = ['svelte'];
@@ -20,6 +22,11 @@ export const SVELTE_HMR_IMPORTS = [
20
22
  'svelte-hmr'
21
23
  ];
22
24
 
25
+ if (isSvelte5) {
26
+ SVELTE_IMPORTS.push('svelte/server', 'svelte/internal/server', 'svelte/legacy');
27
+ SVELTE_HMR_IMPORTS.length = 0; // truncate, svelte-hmr isn't used with svelte5
28
+ }
29
+
23
30
  export const SVELTE_EXPORT_CONDITIONS = ['svelte'];
24
31
 
25
32
  export const FAQ_LINK_MISSING_EXPORTS_CONDITION =
@@ -1,7 +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 { isSvelte5 } from './svelte-version.js';
5
6
 
6
7
  /**
7
8
  * @typedef {NonNullable<import('vite').DepOptimizationOptions['esbuildOptions']>} EsbuildOptions
@@ -10,6 +11,8 @@ import { toESBuildError } from './error.js';
10
11
 
11
12
  export const facadeEsbuildSveltePluginName = 'vite-plugin-svelte:facade';
12
13
 
14
+ const svelteModuleExtension = '.svelte.js';
15
+
13
16
  /**
14
17
  * @param {import('../types/options.d.ts').ResolvedOptions} options
15
18
  * @returns {EsbuildPlugin}
@@ -23,6 +26,9 @@ export function esbuildSveltePlugin(options) {
23
26
  if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
24
27
 
25
28
  const svelteExtensions = (options.extensions ?? ['.svelte']).map((ext) => ext.slice(1));
29
+ if (isSvelte5) {
30
+ svelteExtensions.push(svelteModuleExtension.slice(1));
31
+ }
26
32
  const svelteFilter = new RegExp('\\.(' + svelteExtensions.join('|') + ')(\\?.*)?$');
27
33
  /** @type {import('../types/vite-plugin-svelte-stats.d.ts').StatCollection | undefined} */
28
34
  let statsCollection;
@@ -54,6 +60,20 @@ export function esbuildSveltePlugin(options) {
54
60
  * @returns {Promise<string>}
55
61
  */
56
62
  async function compileSvelte(options, { filename, code }, statsCollection) {
63
+ if (isSvelte5 && filename.endsWith(svelteModuleExtension)) {
64
+ const endStat = statsCollection?.start(filename);
65
+ // @ts-expect-error compileModule does not exist in svelte4
66
+ const compiled = svelte.compileModule(code, {
67
+ filename,
68
+ generate: 'client'
69
+ });
70
+ if (endStat) {
71
+ endStat();
72
+ }
73
+ return compiled.js.map
74
+ ? compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl()
75
+ : compiled.js.code;
76
+ }
57
77
  let css = options.compilerOptions.css;
58
78
  if (css !== 'none') {
59
79
  // TODO ideally we'd be able to externalize prebundled styles too, but for now always put them in the js
@@ -64,14 +84,15 @@ async function compileSvelte(options, { filename, code }, statsCollection) {
64
84
  ...options.compilerOptions,
65
85
  css,
66
86
  filename,
67
- generate: 'dom'
87
+ // @ts-expect-error svelte4 uses 'dom', svelte5 uses 'client'
88
+ generate: isSvelte5 ? 'client' : 'dom'
68
89
  };
69
90
 
70
91
  let preprocessed;
71
92
 
72
93
  if (options.preprocess) {
73
94
  try {
74
- preprocessed = await preprocess(code, options.preprocess, { filename });
95
+ preprocessed = await svelte.preprocess(code, options.preprocess, { filename });
75
96
  } catch (e) {
76
97
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
77
98
  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,49 @@ export function buildIdParser(options) {
184
184
  }
185
185
  };
186
186
  }
187
+
188
+ /**
189
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
190
+ * @returns {import('../types/id.d.ts').ModuleIdParser}
191
+ */
192
+ export function buildModuleIdParser(options) {
193
+ const { include, exclude, extensions } = options?.experimental?.compileModule ?? {};
194
+ const root = options.root;
195
+ const normalizedRoot = normalizePath(root);
196
+ const filter = buildFilter(include, exclude, extensions ?? ['.svelte.js', '.svelte.ts']);
197
+ return (id, ssr, timestamp = Date.now()) => {
198
+ const { filename, rawQuery } = splitId(id);
199
+ if (filter(filename)) {
200
+ return parseToSvelteModuleRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr);
201
+ }
202
+ };
203
+ }
204
+
205
+ /**
206
+ * @param {string} id
207
+ * @param {string} filename
208
+ * @param {string} rawQuery
209
+ * @param {string} root
210
+ * @param {number} timestamp
211
+ * @param {boolean} ssr
212
+ * @returns {import('../types/id.d.ts').SvelteModuleRequest | undefined}
213
+ */
214
+ function parseToSvelteModuleRequest(id, filename, rawQuery, root, timestamp, ssr) {
215
+ const query = parseRequestQuery(rawQuery);
216
+
217
+ if (query.url || query.raw || query.direct) {
218
+ // skip requests with special vite tags
219
+ return;
220
+ }
221
+
222
+ const normalizedFilename = normalize(filename, root);
223
+
224
+ return {
225
+ id,
226
+ filename,
227
+ normalizedFilename,
228
+ query,
229
+ timestamp,
230
+ ssr
231
+ };
232
+ }
@@ -1,7 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import { toRollupError } from './error.js';
3
3
  import { log } from './log.js';
4
-
4
+ import { isSvelte4 } from './svelte-version.js';
5
5
  /**
6
6
  * utility function to compile ?raw and ?direct requests in load hook
7
7
  *
@@ -18,15 +18,15 @@ export async function loadRaw(svelteRequest, compileSvelte, options) {
18
18
  const source = fs.readFileSync(filename, 'utf-8');
19
19
  try {
20
20
  //avoid compileSvelte doing extra ssr stuff unless requested
21
- svelteRequest.ssr = query.compilerOptions?.generate === 'ssr';
21
+ //@ts-ignore //@ts-expect-error generate value differs between svelte4 and 5
22
+ svelteRequest.ssr = query.compilerOptions?.generate === (isSvelte4 ? 'ssr' : 'server');
22
23
  const type = query.type;
23
24
  compileData = await compileSvelte(svelteRequest, source, {
24
25
  ...options,
25
26
  // don't use dynamic vite-plugin-svelte defaults here to ensure stable result between ssr,dev and build
26
27
  compilerOptions: {
27
28
  dev: false,
28
- css: false,
29
- hydratable: false,
29
+ css: 'external',
30
30
  enableSourcemap: query.sourcemap
31
31
  ? {
32
32
  js: type === 'script' || type === 'all',
package/src/utils/log.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-console */
2
2
  import { cyan, red, yellow } from 'kleur/colors';
3
3
  import debug from 'debug';
4
+ import { VERSION } from 'svelte/compiler';
4
5
 
5
6
  /** @type {import('../types/log.d.ts').LogLevel[]} */
6
7
  const levels = ['debug', 'info', 'warn', 'error', 'silent'];
@@ -126,7 +127,7 @@ export const log = {
126
127
  };
127
128
 
128
129
  /**
129
- * @param {import('../types/id.d.ts').SvelteRequest} svelteRequest
130
+ * @param {import('../types/id.d.ts').SvelteRequest | import('../types/id.d.ts').SvelteModuleRequest} svelteRequest
130
131
  * @param {import('svelte/types/compiler/interfaces').Warning[]} warnings
131
132
  * @param {import('../types/options.d.ts').ResolvedOptions} options
132
133
  */
@@ -259,3 +260,12 @@ export function buildExtendedLogMessage(w) {
259
260
  export function isDebugNamespaceEnabled(namespace) {
260
261
  return debug.enabled(`${prefix}:${namespace}`);
261
262
  }
263
+
264
+ export function logSvelte5Warning() {
265
+ const notice = `Your are using Svelte ${VERSION}. Svelte 5 support is experimental, breaking changes can occur in any release until this notice is removed.`;
266
+ const wip = [
267
+ 'svelte-inspector is disabled until dev mode implements node to code mapping',
268
+ 'hmr for .svelte files is disabled until hmr api is implemented'
269
+ ];
270
+ log.warn(`${notice}\nwork in progress:\n - ${wip.join('\n - ')}\n`);
271
+ }
@@ -26,6 +26,7 @@ import {
26
26
  import { isCommonDepWithoutSvelteField } from './dependencies.js';
27
27
  import { VitePluginSvelteStats } from './vite-plugin-svelte-stats.js';
28
28
  import { VitePluginSvelteCache } from './vite-plugin-svelte-cache.js';
29
+ import { isSvelte5 } from './svelte-version.js';
29
30
 
30
31
  const allowedPluginOptions = new Set([
31
32
  'include',
@@ -229,6 +230,10 @@ export function resolveOptions(preResolveOptions, viteConfig, cache) {
229
230
  * @param {import('../types/options.d.ts').ResolvedOptions} options
230
231
  */
231
232
  function enforceOptionsForHmr(options) {
233
+ if (isSvelte5) {
234
+ // TODO add hmr options for svelte5 once it is supported and update utils/log.js#logSvelte5Warning
235
+ options.hot = false;
236
+ }
232
237
  if (options.hot) {
233
238
  if (!options.compilerOptions.dev) {
234
239
  log.warn('hmr is enabled but compilerOptions.dev is false, forcing it to true');
@@ -3,4 +3,9 @@ import { VERSION } from 'svelte/compiler';
3
3
  /**
4
4
  * @type {boolean}
5
5
  */
6
- export const isSvelte3 = VERSION.startsWith('3.');
6
+ export const isSvelte4 = VERSION.startsWith('4.');
7
+
8
+ /**
9
+ * @type {boolean}
10
+ */
11
+ export const isSvelte5 = VERSION.startsWith('5.');
@@ -5,7 +5,7 @@ import path from 'node:path';
5
5
 
6
6
  /**
7
7
  * @param {import('../types/options.d.ts').ResolvedOptions} options
8
- * @param {import('./vite-plugin-svelte-cache').VitePluginSvelteCache} cache
8
+ * @param {import('./vite-plugin-svelte-cache.js').VitePluginSvelteCache} cache
9
9
  * @param {import('../types/id.d.ts').IdParser} requestParser
10
10
  * @returns {void}
11
11
  */
package/types/index.d.ts CHANGED
@@ -173,6 +173,14 @@ declare module '@sveltejs/vite-plugin-svelte' {
173
173
  * @default false
174
174
  */
175
175
  disableSvelteResolveWarnings?: boolean;
176
+
177
+ compileModule?: CompileModuleOptions;
178
+ }
179
+
180
+ interface CompileModuleOptions {
181
+ extensions?: string[];
182
+ include?: Arrayable<string>;
183
+ exclude?: Arrayable<string>;
176
184
  }
177
185
 
178
186
  type Arrayable<T> = T | T[];
@@ -21,5 +21,5 @@
21
21
  null,
22
22
  null
23
23
  ],
24
- "mappings": ";;;;;aAMYA,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2HFC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAmDZC,qBAAqBA;;;;iBC1JtBC,MAAMA;iBCXNC,cAAcA;iBCgBRC,gBAAgBA"
24
+ "mappings": ";;;;;aAMYA,OAAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2HFC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA2DZC,qBAAqBA;;;;iBChKtBC,MAAMA;iBCbNC,cAAcA;iBCgBRC,gBAAgBA"
25
25
  }