@sveltejs/vite-plugin-svelte 6.0.0-next.1 → 6.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.
@@ -0,0 +1,181 @@
1
+ import { log } from '../utils/log.js';
2
+ import { setupWatchers } from '../utils/watch.js';
3
+ import { SVELTE_VIRTUAL_STYLE_ID_REGEX } from '../utils/constants.js';
4
+
5
+ /**
6
+ * @param {import('../types/plugin-api.d.ts').PluginAPI} api
7
+ * @returns {import('vite').Plugin}
8
+ */
9
+ export function hotUpdate(api) {
10
+ /**
11
+ * @type {import("../types/options.js").ResolvedOptions}
12
+ */
13
+ let options;
14
+ /**
15
+ * @type {import('../types/id.d.ts').IdParser}
16
+ */
17
+ let idParser;
18
+
19
+ /**
20
+ *
21
+ * @type {Map<string|null,string>}
22
+ */
23
+ const transformResultCache = new Map();
24
+
25
+ /** @type {import('vite').Plugin} */
26
+ const plugin = {
27
+ name: 'vite-plugin-svelte:hot-update',
28
+ enforce: 'post',
29
+ configResolved() {
30
+ options = api.options;
31
+ idParser = api.idParser;
32
+
33
+ // @ts-expect-error
34
+ plugin.transform.filter = {
35
+ id: {
36
+ // reinclude virtual styles to get their output
37
+ include: [...api.idFilter.id.include, SVELTE_VIRTUAL_STYLE_ID_REGEX],
38
+ exclude: [
39
+ // ignore files in node_modules, we don't hot update them
40
+ /\/node_modules\//,
41
+ // remove style exclusion
42
+ ...api.idFilter.id.exclude.filter((filter) => filter !== SVELTE_VIRTUAL_STYLE_ID_REGEX)
43
+ ]
44
+ }
45
+ };
46
+ },
47
+
48
+ applyToEnvironment(env) {
49
+ // we only handle updates for client components
50
+ // ssr frameworks have to handle updating/reloading themselves as v-p-s can't know what they prefer
51
+ const hmrEnabled = options.compilerOptions.hmr && options.emitCss;
52
+ return hmrEnabled && env.config.consumer === 'client';
53
+ },
54
+
55
+ configureServer(server) {
56
+ const clientEnvironment = Object.values(server.environments).find(
57
+ (e) => e.config.consumer === 'client'
58
+ );
59
+ if (clientEnvironment) {
60
+ setupWatchers(options);
61
+ } else {
62
+ log.warn(
63
+ 'No client environment found, not adding watchers for svelte config and preprocessor dependencies'
64
+ );
65
+ }
66
+ },
67
+
68
+ buildStart() {
69
+ transformResultCache.clear();
70
+ },
71
+
72
+ transform: {
73
+ order: 'post',
74
+ handler(code, id) {
75
+ transformResultCache.set(id, code);
76
+ }
77
+ },
78
+ hotUpdate: {
79
+ order: 'post',
80
+ async handler(ctx) {
81
+ const svelteRequest = idParser(ctx.file, false, ctx.timestamp);
82
+ if (svelteRequest) {
83
+ const { modules } = ctx;
84
+ const svelteModules = [];
85
+ const nonSvelteModules = [];
86
+ for (const mod of modules) {
87
+ if (transformResultCache.has(mod.id)) {
88
+ svelteModules.push(mod);
89
+ } else {
90
+ nonSvelteModules.push(mod);
91
+ }
92
+ }
93
+
94
+ if (svelteModules.length === 0) {
95
+ return; // nothing to do for us
96
+ }
97
+ const affectedModules = [];
98
+ const prevResults = svelteModules.map((m) => transformResultCache.get(m.id));
99
+ for (let i = 0; i < svelteModules.length; i++) {
100
+ const mod = svelteModules[i];
101
+ const prev = prevResults[i];
102
+ await this.environment.transformRequest(mod.url);
103
+ const next = transformResultCache.get(mod.id);
104
+ if (hasCodeChanged(prev, next, mod.id)) {
105
+ affectedModules.push(mod);
106
+ } else {
107
+ log.debug(
108
+ `skipping hot update for ${mod.id} because result is unchanged`,
109
+ undefined,
110
+ 'hmr'
111
+ );
112
+ }
113
+ }
114
+ log.debug(
115
+ `hotUpdate for ${svelteRequest.id} result: [${affectedModules.map((m) => m.id).join(', ')}]`,
116
+ undefined,
117
+ 'hmr'
118
+ );
119
+ return [...affectedModules, ...nonSvelteModules];
120
+ }
121
+ }
122
+ }
123
+ };
124
+
125
+ return plugin;
126
+ }
127
+
128
+ /**
129
+ * @param {string | undefined | null} prev
130
+ * @param {string | undefined | null} next
131
+ * @param {string | null} id
132
+ * @returns {boolean}
133
+ */
134
+ function hasCodeChanged(prev, next, id) {
135
+ const isStrictEqual = nullSafeEqual(prev, next);
136
+ if (isStrictEqual) {
137
+ return false;
138
+ }
139
+ const isLooseEqual = nullSafeEqual(normalize(prev), normalize(next));
140
+ if (!isStrictEqual && isLooseEqual) {
141
+ log.debug(
142
+ `ignoring compiler output change for ${id} as it is equal to previous output after normalization`,
143
+ undefined,
144
+ 'hmr'
145
+ );
146
+ }
147
+ return !isLooseEqual;
148
+ }
149
+
150
+ /**
151
+ * @param {string | null | undefined} prev
152
+ * @param {string | null | undefined} next
153
+ * @returns {boolean}
154
+ */
155
+ function nullSafeEqual(prev, next) {
156
+ return (prev == null && next == null) || (prev != null && next != null && prev === next);
157
+ }
158
+
159
+ /**
160
+ * remove code that only changes metadata and does not require a js update for the component to keep working
161
+ *
162
+ * 1) location numbers argument from $.add_locations calls in svelte output eg [[1,2],[3,4]]
163
+ * 2) timestamp queries added to imports by vite eg ?t=0123456789123
164
+ *
165
+ * @param {string | null | undefined } code
166
+ * @returns {string | null | undefined}
167
+ */
168
+ function normalize(code) {
169
+ if (code == null) {
170
+ return code;
171
+ }
172
+
173
+ return (
174
+ code
175
+ // svelte5 $.add_locations line numbers argument [[1,2],[3,4]]
176
+ // uses matching group replace to keep the template argument intact
177
+ .replace(/(\$\.add_locations\(.*), (\[\[[\d, [\]]+]])\)/g, '$1, []')
178
+ // vite import analysis timestamp queries, ?t=0123456789123&
179
+ .replace(/[?&]t=\d{13}\b/g, '')
180
+ );
181
+ }
@@ -0,0 +1,44 @@
1
+ import { log } from '../utils/log.js';
2
+ import { SVELTE_VIRTUAL_STYLE_ID_REGEX } from '../utils/constants.js';
3
+
4
+ const filter = { id: SVELTE_VIRTUAL_STYLE_ID_REGEX };
5
+
6
+ /**
7
+ * @param {import('../types/plugin-api.d.ts').PluginAPI} api
8
+ * @returns {import('vite').Plugin}
9
+ */
10
+ export function loadCompiledCss(api) {
11
+ return {
12
+ name: 'vite-plugin-svelte:load-compiled-css',
13
+
14
+ resolveId: {
15
+ filter, // same filter in load to ensure minimal work
16
+ handler(id) {
17
+ log.debug(`resolveId resolved virtual css module ${id}`, undefined, 'resolve');
18
+ return id;
19
+ }
20
+ },
21
+ load: {
22
+ filter,
23
+ async handler(id) {
24
+ const ssr = this.environment.config.consumer === 'server';
25
+ const svelteRequest = api.idParser(id, ssr);
26
+ if (!svelteRequest) {
27
+ return;
28
+ }
29
+ const cachedCss = this.getModuleInfo(svelteRequest.filename)?.meta.svelte?.css;
30
+ if (cachedCss) {
31
+ const { hasGlobal, ...css } = cachedCss;
32
+ if (hasGlobal === false) {
33
+ // hasGlobal was added in svelte 5.26.0, so make sure it is boolean false
34
+ css.meta ??= {};
35
+ css.meta.vite ??= {};
36
+ css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default'];
37
+ }
38
+ css.moduleType = 'css';
39
+ return css;
40
+ }
41
+ }
42
+ }
43
+ };
44
+ }
@@ -0,0 +1,42 @@
1
+ import fs from 'node:fs';
2
+ import { log } from '../utils/log.js';
3
+
4
+ /**
5
+ * if svelte config includes files that vite treats as assets (e.g. .svg)
6
+ * we have to manually load them to avoid getting urls
7
+ *
8
+ * @param {import('../types/plugin-api.d.ts').PluginAPI} api
9
+ * @returns {import('vite').Plugin}
10
+ */
11
+ export function loadCustom(api) {
12
+ /** @type {import('vite').Plugin} */
13
+ const plugin = {
14
+ name: 'vite-plugin-svelte:load-custom',
15
+ enforce: 'pre', // must come before vites own asset handling or custom extensions like .svg won't work
16
+ configResolved() {
17
+ //@ts-expect-error load defined below but filter not in type
18
+ plugin.load.filter = api.idFilter;
19
+ },
20
+
21
+ load: {
22
+ //filter: is set in configResolved
23
+ async handler(id) {
24
+ const config = this.environment.config;
25
+ const ssr = config.consumer === 'server';
26
+ const svelteRequest = api.idParser(id, ssr);
27
+ if (svelteRequest) {
28
+ const { filename, query } = svelteRequest;
29
+ if (!query.url && config.assetsInclude(filename)) {
30
+ log.debug(
31
+ `loading ${filename} to prevent vite asset handling to turn it into a url by default`,
32
+ undefined,
33
+ 'load'
34
+ );
35
+ return fs.readFileSync(filename, 'utf-8');
36
+ }
37
+ }
38
+ }
39
+ }
40
+ };
41
+ return plugin;
42
+ }
@@ -0,0 +1,205 @@
1
+ import { toRollupError } from '../utils/error.js';
2
+ import { mapToRelative } from '../utils/sourcemaps.js';
3
+ import * as svelte from 'svelte/compiler';
4
+ import { log } from '../utils/log.js';
5
+ import { arraify } from '../utils/options.js';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+
9
+ /**
10
+ * @param {import('../types/plugin-api.d.ts').PluginAPI} api
11
+ * @returns {import('vite').Plugin}
12
+ */
13
+ export function preprocess(api) {
14
+ /**
15
+ * @type {import("../types/options.js").ResolvedOptions}
16
+ */
17
+ let options;
18
+
19
+ /**
20
+ * @type {DependenciesCache}
21
+ */
22
+ let dependenciesCache;
23
+
24
+ /**
25
+ * @type {import("../types/compile.d.ts").PreprocessSvelte}
26
+ */
27
+ let preprocessSvelte;
28
+
29
+ /** @type {import('vite').Plugin} */
30
+ const plugin = {
31
+ name: 'vite-plugin-svelte:preprocess',
32
+ enforce: 'pre',
33
+ configResolved(c) {
34
+ //@ts-expect-error defined below but filter not in type
35
+ plugin.transform.filter = api.idFilter;
36
+ options = api.options;
37
+ if (arraify(options.preprocess).length > 0) {
38
+ preprocessSvelte = createPreprocessSvelte(options, c);
39
+ } else {
40
+ log.debug(
41
+ `disabling ${plugin.name} because no preprocessor is configured`,
42
+ undefined,
43
+ 'preprocess'
44
+ );
45
+ delete plugin.transform;
46
+ }
47
+ },
48
+ configureServer(server) {
49
+ dependenciesCache = new DependenciesCache(server);
50
+ },
51
+ buildStart() {
52
+ dependenciesCache?.clear();
53
+ },
54
+ transform: {
55
+ async handler(code, id) {
56
+ const ssr = this.environment.config.consumer === 'server';
57
+ const svelteRequest = api.idParser(id, ssr);
58
+ if (!svelteRequest) {
59
+ return;
60
+ }
61
+ try {
62
+ const preprocessed = await preprocessSvelte(svelteRequest, code, options);
63
+ dependenciesCache?.update(svelteRequest, preprocessed?.dependencies ?? []);
64
+ if (!preprocessed) {
65
+ return;
66
+ }
67
+ if (options.isBuild && this.environment.config.build.watch && preprocessed.dependencies) {
68
+ for (const dep of preprocessed.dependencies) {
69
+ this.addWatchFile(dep);
70
+ }
71
+ }
72
+
73
+ /** @type {import('vite').Rollup.SourceDescription}*/
74
+ const result = { code: preprocessed.code };
75
+ if (preprocessed.map) {
76
+ // @ts-expect-error type differs but should work
77
+ result.map = preprocessed.map;
78
+ }
79
+ return result;
80
+ } catch (e) {
81
+ throw toRollupError(e, options);
82
+ }
83
+ }
84
+ }
85
+ };
86
+ return plugin;
87
+ }
88
+ /**
89
+ * @param {import("../types/options.js").ResolvedOptions} options
90
+ * @param {import("vite").ResolvedConfig} resolvedConfig
91
+ * @returns {import('../types/compile.d.ts').PreprocessSvelte}
92
+ */
93
+ function createPreprocessSvelte(options, resolvedConfig) {
94
+ /** @type {Array<import('svelte/compiler').PreprocessorGroup>} */
95
+ const preprocessors = arraify(options.preprocess);
96
+
97
+ for (const preprocessor of preprocessors) {
98
+ if (preprocessor.style && '__resolvedConfig' in preprocessor.style) {
99
+ preprocessor.style.__resolvedConfig = resolvedConfig;
100
+ }
101
+ }
102
+
103
+ /** @type {import('../types/compile.d.ts').PreprocessSvelte} */
104
+ return async function preprocessSvelte(svelteRequest, code) {
105
+ const { filename } = svelteRequest;
106
+ let preprocessed;
107
+ if (preprocessors && preprocessors.length > 0) {
108
+ try {
109
+ preprocessed = await svelte.preprocess(code, preprocessors, { filename }); // full filename here so postcss works
110
+ } catch (e) {
111
+ e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
112
+ throw e;
113
+ }
114
+ if (typeof preprocessed?.map === 'object') {
115
+ mapToRelative(preprocessed?.map, filename);
116
+ }
117
+ return preprocessed;
118
+ }
119
+ };
120
+ }
121
+
122
+ /**
123
+ * @class
124
+ *
125
+ * caches dependencies of preprocessed files and emit change events on dependants
126
+ */
127
+ class DependenciesCache {
128
+ /** @type {Map<string, string[]>} */
129
+ #dependencies = new Map();
130
+ /** @type {Map<string, Set<string>>} */
131
+ #dependants = new Map();
132
+
133
+ /** @type {import('vite').ViteDevServer} */
134
+ #server;
135
+ /**
136
+ *
137
+ * @param {import('vite').ViteDevServer} server
138
+ */
139
+ constructor(server) {
140
+ this.#server = server;
141
+ /** @type {(filename: string) => void} */
142
+ const emitChangeEventOnDependants = (filename) => {
143
+ const dependants = this.#dependants.get(filename);
144
+ dependants?.forEach((dependant) => {
145
+ if (fs.existsSync(dependant)) {
146
+ log.debug(
147
+ `emitting virtual change event for "${dependant}" because dependency "${filename}" changed`,
148
+ undefined,
149
+ 'hmr'
150
+ );
151
+ server.watcher.emit('change', dependant);
152
+ }
153
+ });
154
+ };
155
+ server.watcher.on('change', emitChangeEventOnDependants);
156
+ server.watcher.on('unlink', emitChangeEventOnDependants);
157
+ }
158
+
159
+ /**
160
+ * @param {string} file
161
+ */
162
+ #ensureWatchedFile(file) {
163
+ const root = this.#server.config.root;
164
+ if (
165
+ file &&
166
+ // only need to watch if out of root
167
+ !file.startsWith(root + '/') &&
168
+ // some rollup plugins use null bytes for private resolved Ids
169
+ !file.includes('\0') &&
170
+ fs.existsSync(file)
171
+ ) {
172
+ // resolve file to normalized system path
173
+ this.#server.watcher.add(path.resolve(file));
174
+ }
175
+ }
176
+
177
+ clear() {
178
+ this.#dependencies.clear();
179
+ this.#dependants.clear();
180
+ }
181
+
182
+ /**
183
+ *
184
+ * @param {import('../types/id.d.ts').SvelteRequest} svelteRequest
185
+ * @param {string[]} dependencies
186
+ */
187
+ update(svelteRequest, dependencies) {
188
+ const id = svelteRequest.normalizedFilename;
189
+ const prevDependencies = this.#dependencies.get(id) || [];
190
+
191
+ this.#dependencies.set(id, dependencies);
192
+ const removed = prevDependencies.filter((d) => !dependencies.includes(d));
193
+ const added = dependencies.filter((d) => !prevDependencies.includes(d));
194
+ added.forEach((d) => {
195
+ this.#ensureWatchedFile(d);
196
+ if (!this.#dependants.has(d)) {
197
+ this.#dependants.set(d, new Set());
198
+ }
199
+ /** @type {Set<string>} */ (this.#dependants.get(d)).add(svelteRequest.filename);
200
+ });
201
+ removed.forEach((d) => {
202
+ /** @type {Set<string>} */ (this.#dependants.get(d)).delete(svelteRequest.filename);
203
+ });
204
+ }
205
+ }
@@ -1,9 +1,14 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
1
3
  import { readFileSync } from 'node:fs';
2
4
  import * as svelte from 'svelte/compiler';
3
- import { log } from './log.js';
4
- import { toESBuildError, toRollupError } from './error.js';
5
- import { safeBase64Hash } from './hash.js';
6
- import { normalize } from './id.js';
5
+ import { log } from '../utils/log.js';
6
+ import { toESBuildError, toRollupError } from '../utils/error.js';
7
+ import { safeBase64Hash } from '../utils/hash.js';
8
+ import { normalize } from '../utils/id.js';
9
+ import * as vite from 'vite';
10
+ // @ts-ignore not typed on vite
11
+ const { rolldownVersion } = vite;
7
12
 
8
13
  /**
9
14
  * @typedef {NonNullable<import('vite').DepOptimizationOptions['esbuildOptions']>} EsbuildOptions
@@ -13,14 +18,86 @@ import { normalize } from './id.js';
13
18
  * @typedef {NonNullable<import('vite').Rollup.Plugin>} RollupPlugin
14
19
  */
15
20
 
16
- export const optimizeSveltePluginName = 'vite-plugin-svelte:optimize';
17
- export const optimizeSvelteModulePluginName = 'vite-plugin-svelte-module:optimize';
21
+ const optimizeSveltePluginName = 'vite-plugin-svelte:optimize';
22
+ const optimizeSvelteModulePluginName = 'vite-plugin-svelte:optimize-module';
23
+
24
+ /**
25
+ * @param {import('../types/plugin-api.d.ts').PluginAPI} api
26
+ * @returns {import('vite').Plugin}
27
+ */
28
+ export function setupOptimizer(api) {
29
+ /** @type {import('vite').ResolvedConfig} */
30
+ let viteConfig;
31
+
32
+ return {
33
+ name: 'vite-plugin-svelte:setup-optimizer',
34
+ apply: 'serve',
35
+ config() {
36
+ /** @type {import('vite').UserConfig['optimizeDeps']} */
37
+ const optimizeDeps = {
38
+ // Experimental Vite API to allow these extensions to be scanned and prebundled
39
+ extensions: ['.svelte']
40
+ };
41
+ // Add optimizer plugins to prebundle Svelte files.
42
+ // Currently, a placeholder as more information is needed after Vite config is resolved,
43
+ // the added plugins are patched in configResolved below
44
+ if (rolldownVersion) {
45
+ //@ts-ignore rolldown types not finished
46
+ optimizeDeps.rollupOptions = {
47
+ plugins: [
48
+ placeholderRolldownOptimizerPlugin(optimizeSveltePluginName),
49
+ placeholderRolldownOptimizerPlugin(optimizeSvelteModulePluginName)
50
+ ]
51
+ };
52
+ } else {
53
+ optimizeDeps.esbuildOptions = {
54
+ plugins: [
55
+ { name: optimizeSveltePluginName, setup: () => {} },
56
+ { name: optimizeSvelteModulePluginName, setup: () => {} }
57
+ ]
58
+ };
59
+ }
60
+ return { optimizeDeps };
61
+ },
62
+ configResolved(c) {
63
+ viteConfig = c;
64
+ const optimizeDeps = c.optimizeDeps;
65
+ if (rolldownVersion) {
66
+ const plugins =
67
+ // @ts-expect-error not typed
68
+ optimizeDeps.rollupOptions?.plugins?.filter((p) =>
69
+ [optimizeSveltePluginName, optimizeSvelteModulePluginName].includes(p.name)
70
+ ) ?? [];
71
+ for (const plugin of plugins) {
72
+ patchRolldownOptimizerPlugin(plugin, api.options);
73
+ }
74
+ } else {
75
+ const plugins =
76
+ optimizeDeps.esbuildOptions?.plugins?.filter((p) =>
77
+ [optimizeSveltePluginName, optimizeSvelteModulePluginName].includes(p.name)
78
+ ) ?? [];
79
+ for (const plugin of plugins) {
80
+ patchESBuildOptimizerPlugin(plugin, api.options);
81
+ }
82
+ }
83
+ },
84
+ async buildStart() {
85
+ if (!api.options.prebundleSvelteLibraries) return;
86
+ const changed = await svelteMetadataChanged(viteConfig.cacheDir, api.options);
87
+ if (changed) {
88
+ // Force Vite to optimize again. Although we mutate the config here, it works because
89
+ // Vite's optimizer runs after `buildStart()`.
90
+ viteConfig.optimizeDeps.force = true;
91
+ }
92
+ }
93
+ };
94
+ }
18
95
 
19
96
  /**
20
97
  * @param {EsbuildPlugin} plugin
21
98
  * @param {import('../types/options.d.ts').ResolvedOptions} options
22
99
  */
23
- export function patchESBuildOptimizerPlugin(plugin, options) {
100
+ function patchESBuildOptimizerPlugin(plugin, options) {
24
101
  const components = plugin.name === optimizeSveltePluginName;
25
102
  const compileFn = components ? compileSvelte : compileSvelteModule;
26
103
  const statsName = components ? 'prebundle library components' : 'prebundle library modules';
@@ -57,7 +134,7 @@ export function patchESBuildOptimizerPlugin(plugin, options) {
57
134
  * @param {RollupPlugin} plugin
58
135
  * @param {import('../types/options.d.ts').ResolvedOptions} options
59
136
  */
60
- export function patchRolldownOptimizerPlugin(plugin, options) {
137
+ function patchRolldownOptimizerPlugin(plugin, options) {
61
138
  const components = plugin.name === optimizeSveltePluginName;
62
139
  const compileFn = components ? compileSvelte : compileSvelteModule;
63
140
  const statsName = components ? 'prebundle library components' : 'prebundle library modules';
@@ -193,3 +270,71 @@ async function compileSvelteModule(options, { filename, code }, statsCollection)
193
270
  moduleType: 'js'
194
271
  };
195
272
  }
273
+
274
+ // List of options that changes the prebundling result
275
+ /** @type {(keyof import('../types/options.d.ts').ResolvedOptions)[]} */
276
+ const PREBUNDLE_SENSITIVE_OPTIONS = [
277
+ 'compilerOptions',
278
+ 'configFile',
279
+ 'experimental',
280
+ 'extensions',
281
+ 'ignorePluginPreprocessors',
282
+ 'preprocess'
283
+ ];
284
+
285
+ /**
286
+ * stores svelte metadata in cache dir and compares if it has changed
287
+ *
288
+ * @param {string} cacheDir
289
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
290
+ * @returns {Promise<boolean>} Whether the Svelte metadata has changed
291
+ */
292
+ async function svelteMetadataChanged(cacheDir, options) {
293
+ const svelteMetadata = generateSvelteMetadata(options);
294
+ const svelteMetadataPath = path.resolve(cacheDir, '_svelte_metadata.json');
295
+
296
+ const currentSvelteMetadata = JSON.stringify(svelteMetadata, (_, value) => {
297
+ // Handle preprocessors
298
+ return typeof value === 'function' ? value.toString() : value;
299
+ });
300
+
301
+ /** @type {string | undefined} */
302
+ let existingSvelteMetadata;
303
+ try {
304
+ existingSvelteMetadata = await fs.readFile(svelteMetadataPath, 'utf8');
305
+ } catch {
306
+ // ignore
307
+ }
308
+
309
+ await fs.mkdir(cacheDir, { recursive: true });
310
+ await fs.writeFile(svelteMetadataPath, currentSvelteMetadata);
311
+ return currentSvelteMetadata !== existingSvelteMetadata;
312
+ }
313
+
314
+ /**
315
+ *
316
+ * @param {string} name
317
+ * @returns {import('vite').Rollup.Plugin}
318
+ */
319
+ function placeholderRolldownOptimizerPlugin(name) {
320
+ return {
321
+ name,
322
+ options() {},
323
+ buildStart() {},
324
+ buildEnd() {},
325
+ transform: { filter: { id: /^$/ }, handler() {} }
326
+ };
327
+ }
328
+
329
+ /**
330
+ * @param {import('../types/options.d.ts').ResolvedOptions} options
331
+ * @returns {Partial<import('../types/options.d.ts').ResolvedOptions>}
332
+ */
333
+ function generateSvelteMetadata(options) {
334
+ /** @type {Record<string, any>} */
335
+ const metadata = {};
336
+ for (const key of PREBUNDLE_SENSITIVE_OPTIONS) {
337
+ metadata[key] = options[key];
338
+ }
339
+ return metadata;
340
+ }
package/src/preprocess.js CHANGED
@@ -6,9 +6,9 @@ const {
6
6
  preprocessCSS,
7
7
  resolveConfig,
8
8
  transformWithEsbuild,
9
- //@ts-expect-error rolldown types don't exist
9
+ //@ts-ignore rolldown types don't exist
10
10
  rolldownVersion,
11
- //@ts-expect-error rolldown types don't exist
11
+ //@ts-ignore rolldown types don't exist
12
12
  transformWithOxc
13
13
  } = vite;
14
14
  /**
@@ -72,8 +72,10 @@ function viteScript() {
72
72
  function viteScriptOxc() {
73
73
  return {
74
74
  async script({ attributes, content, filename = '' }) {
75
- const lang = /** @type {string} */ (attributes.lang);
76
- if (!supportedScriptLangs.includes(lang)) return;
75
+ if (typeof attributes.lang !== 'string' || !supportedScriptLangs.includes(attributes.lang)) {
76
+ return;
77
+ }
78
+ const lang = /** @type {'ts'} */ (attributes.lang);
77
79
  const { code, map } = await transformWithOxc(content, filename, {
78
80
  lang,
79
81
  target: 'esnext'
package/src/public.d.ts CHANGED
@@ -173,6 +173,13 @@ interface ExperimentalOptions {
173
173
  */
174
174
  disableSvelteResolveWarnings?: boolean;
175
175
 
176
+ /**
177
+ * disable api.sveltePreprocess deprecation warnings
178
+ *
179
+ * @default false
180
+ */
181
+ disableApiSveltePreprocessWarnings?: boolean;
182
+
176
183
  compileModule?: CompileModuleOptions;
177
184
  }
178
185