Skip to content

Commit b1dd3ca

Browse files
committed
refactor: add object hook filters to main vite plugin
1 parent 59e082e commit b1dd3ca

File tree

8 files changed

+165
-90
lines changed

8 files changed

+165
-90
lines changed

.changeset/tangy-cars-dress.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': major
3+
---
4+
5+
define filters using object hook syntax and optimize the filter for resolveId
6+
7+
> [!NOTE]
8+
> include logic has changed to files matching `svelteConfig.include` **OR** `svelteConfig.extensions`. Previously only files matching both were loaded and transformed.

packages/vite-plugin-svelte/src/index.js

Lines changed: 84 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector';
44
import { handleHotUpdate } from './handle-hot-update.js';
55
import { log, logCompilerWarnings } from './utils/log.js';
66
import { createCompileSvelte } from './utils/compile.js';
7-
import { buildIdParser, buildModuleIdParser } from './utils/id.js';
7+
import { buildIdFilter, buildIdParser, buildModuleIdParser } from './utils/id.js';
88
import {
99
buildExtraViteConfig,
1010
validateInlineOptions,
@@ -20,6 +20,7 @@ import { saveSvelteMetadata } from './utils/optimizer.js';
2020
import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache.js';
2121
import { loadRaw } from './utils/load-raw.js';
2222
import * as svelteCompiler from 'svelte/compiler';
23+
import { SVELTE_VIRTUAL_STYLE_ID_REGEX } from './utils/constants.js';
2324

2425
/**
2526
* @param {Partial<import('./public.d.ts').Options>} [inlineOptions]
@@ -42,8 +43,13 @@ export function svelte(inlineOptions) {
4243
let viteConfig;
4344
/** @type {import('./types/compile.d.ts').CompileSvelte} */
4445
let compileSvelte;
46+
47+
/** @type {import('./types/id.d.ts').IdFilter} */
48+
let filter = { id: { include: [], exclude: [] } }; // set with correct values in configResolved
49+
4550
/** @type {import('./types/plugin-api.d.ts').PluginAPI} */
4651
const api = {};
52+
4753
/** @type {import('vite').Plugin[]} */
4854
const plugins = [
4955
{
@@ -79,6 +85,7 @@ export function svelte(inlineOptions) {
7985
async configResolved(config) {
8086
options = resolveOptions(options, config, cache);
8187
patchResolvedViteConfig(config, options);
88+
filter = buildIdFilter(options);
8289
requestParser = buildIdParser(options);
8390
compileSvelte = createCompileSvelte();
8491
viteConfig = config;
@@ -102,98 +109,95 @@ export function svelte(inlineOptions) {
102109
setupWatchers(options, cache, requestParser);
103110
},
104111

105-
async load(id, opts) {
106-
const ssr = !!opts?.ssr;
107-
const svelteRequest = requestParser(id, !!ssr);
108-
if (svelteRequest) {
109-
const { filename, query, raw } = svelteRequest;
110-
if (raw) {
111-
const code = await loadRaw(svelteRequest, compileSvelte, options);
112-
// prevent vite from injecting sourcemaps in the results.
113-
return {
114-
code,
115-
map: {
116-
mappings: ''
117-
}
118-
};
119-
} else {
120-
if (query.svelte && query.type === 'style') {
121-
const cachedCss = cache.getCSS(svelteRequest);
122-
if (cachedCss) {
123-
const { hasGlobal, ...css } = cachedCss;
124-
if (hasGlobal === false) {
125-
// hasGlobal was added in svelte 5.26.0, so make sure it is boolean false
126-
css.meta ??= {};
127-
css.meta.vite ??= {};
128-
css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default'];
112+
load: {
113+
filter,
114+
async handler(id, opts) {
115+
const ssr = !!opts?.ssr;
116+
const svelteRequest = requestParser(id, !!ssr);
117+
if (svelteRequest) {
118+
const { filename, query, raw } = svelteRequest;
119+
if (raw) {
120+
const code = await loadRaw(svelteRequest, compileSvelte, options);
121+
// prevent vite from injecting sourcemaps in the results.
122+
return {
123+
code,
124+
map: {
125+
mappings: ''
126+
}
127+
};
128+
} else {
129+
if (query.svelte && query.type === 'style') {
130+
const cachedCss = cache.getCSS(svelteRequest);
131+
if (cachedCss) {
132+
const { hasGlobal, ...css } = cachedCss;
133+
if (hasGlobal === false) {
134+
// hasGlobal was added in svelte 5.26.0, so make sure it is boolean false
135+
css.meta ??= {};
136+
css.meta.vite ??= {};
137+
css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default'];
138+
}
139+
return css;
129140
}
130-
return css;
131141
}
132-
}
133-
// prevent vite asset plugin from loading files as url that should be compiled in transform
134-
if (viteConfig.assetsInclude(filename)) {
135-
log.debug(`load returns raw content for ${filename}`, undefined, 'load');
136-
return fs.readFileSync(filename, 'utf-8');
142+
// prevent vite asset plugin from loading files as url that should be compiled in transform
143+
if (viteConfig.assetsInclude(filename)) {
144+
log.debug(`load returns raw content for ${filename}`, undefined, 'load');
145+
return fs.readFileSync(filename, 'utf-8');
146+
}
137147
}
138148
}
139149
}
140150
},
141151

142-
async resolveId(importee, importer, opts) {
143-
const ssr = !!opts?.ssr;
144-
const svelteRequest = requestParser(importee, ssr);
145-
if (svelteRequest?.query.svelte) {
146-
if (
147-
svelteRequest.query.type === 'style' &&
148-
!svelteRequest.raw &&
149-
!svelteRequest.query.inline
150-
) {
151-
// return cssId with root prefix so postcss pipeline of vite finds the directory correctly
152-
// see https://github.com/sveltejs/vite-plugin-svelte/issues/14
153-
log.debug(
154-
`resolveId resolved virtual css module ${svelteRequest.cssId}`,
155-
undefined,
156-
'resolve'
157-
);
158-
return svelteRequest.cssId;
159-
}
152+
resolveId: {
153+
// we don't use our generic filter here but a reduced one that only matches our virtual css
154+
filter: { id: SVELTE_VIRTUAL_STYLE_ID_REGEX },
155+
handler(id) {
156+
// return cssId with root prefix so postcss pipeline of vite finds the directory correctly
157+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/14
158+
log.debug(`resolveId resolved virtual css module ${id}`, undefined, 'resolve');
159+
// TODO: do we have to repeat the dance for constructing the virtual id here? our transform added it that way
160+
return id;
160161
}
161162
},
162163

163-
async transform(code, id, opts) {
164-
const ssr = !!opts?.ssr;
165-
const svelteRequest = requestParser(id, ssr);
166-
if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) {
167-
return;
168-
}
169-
let compileData;
170-
try {
171-
compileData = await compileSvelte(svelteRequest, code, options);
172-
} catch (e) {
173-
cache.setError(svelteRequest, e);
174-
throw toRollupError(e, options);
175-
}
176-
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
177-
cache.update(compileData);
178-
if (compileData.dependencies?.length) {
179-
if (options.server) {
180-
for (const dep of compileData.dependencies) {
181-
ensureWatchedFile(options.server.watcher, dep, options.root);
182-
}
183-
} else if (options.isBuild && viteConfig.build.watch) {
184-
for (const dep of compileData.dependencies) {
185-
this.addWatchFile(dep);
186-
}
164+
transform: {
165+
filter,
166+
async handler(code, id, opts) {
167+
const ssr = !!opts?.ssr;
168+
const svelteRequest = requestParser(id, ssr);
169+
if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) {
170+
return;
187171
}
188-
}
189-
return {
190-
...compileData.compiled.js,
191-
meta: {
192-
vite: {
193-
lang: compileData.lang
172+
let compileData;
173+
try {
174+
compileData = await compileSvelte(svelteRequest, code, options);
175+
} catch (e) {
176+
cache.setError(svelteRequest, e);
177+
throw toRollupError(e, options);
178+
}
179+
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
180+
cache.update(compileData);
181+
if (compileData.dependencies?.length) {
182+
if (options.server) {
183+
for (const dep of compileData.dependencies) {
184+
ensureWatchedFile(options.server.watcher, dep, options.root);
185+
}
186+
} else if (options.isBuild && viteConfig.build.watch) {
187+
for (const dep of compileData.dependencies) {
188+
this.addWatchFile(dep);
189+
}
194190
}
195191
}
196-
};
192+
return {
193+
...compileData.compiled.js,
194+
meta: {
195+
vite: {
196+
lang: compileData.lang
197+
}
198+
}
199+
};
200+
}
197201
},
198202

199203
handleHotUpdate(ctx) {

packages/vite-plugin-svelte/src/public.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ export interface PluginOptions {
2222
*
2323
* @see https://github.com/micromatch/picomatch
2424
*/
25-
include?: Arrayable<string>;
25+
include?: Arrayable<string | RegExp>;
2626
/**
2727
* A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the
2828
* plugin. By default, no files are ignored.
2929
*
3030
* @see https://github.com/micromatch/picomatch
3131
*/
32-
exclude?: Arrayable<string>;
32+
exclude?: Arrayable<string | RegExp>;
3333
/**
3434
* Emit Svelte styles as virtual CSS files for Vite and other plugins to process
3535
*
@@ -187,8 +187,8 @@ interface CompileModuleOptions {
187187
* @default ['.ts','.js']
188188
*/
189189
extensions?: string[];
190-
include?: Arrayable<string>;
191-
exclude?: Arrayable<string>;
190+
include?: Arrayable<string | RegExp>;
191+
exclude?: Arrayable<string | RegExp>;
192192
}
193193

194194
type Arrayable<T> = T | T[];

packages/vite-plugin-svelte/src/types/id.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export interface SvelteModuleRequest {
3939
}
4040

4141
export type IdParser = (id: string, ssr: boolean, timestamp?: number) => SvelteRequest | undefined;
42+
43+
export type IdFilter = {
44+
id: {
45+
include: Array<string | RegExp>;
46+
exclude: Array<string | RegExp>;
47+
};
48+
};
4249
export type ModuleIdParser = (
4350
id: string,
4451
ssr: boolean,

packages/vite-plugin-svelte/src/utils/constants.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ export const FAQ_LINK_MISSING_EXPORTS_CONDITION =
2929
export const DEFAULT_SVELTE_EXT = ['.svelte'];
3030
export const DEFAULT_SVELTE_MODULE_INFIX = ['.svelte.'];
3131
export const DEFAULT_SVELTE_MODULE_EXT = ['.js', '.ts'];
32+
33+
export const SVELTE_VIRTUAL_STYLE_SUFFIX = '?svelte&type=style&lang.css';
34+
export const SVELTE_VIRTUAL_STYLE_ID_REGEX = new RegExp(
35+
`${SVELTE_VIRTUAL_STYLE_SUFFIX.replace(/[?.]/g, '\\$&')}$`
36+
);

packages/vite-plugin-svelte/src/utils/id.js

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ import fs from 'node:fs';
33
import path from 'node:path';
44
import process from 'node:process';
55
import { log } from './log.js';
6-
import { DEFAULT_SVELTE_MODULE_EXT, DEFAULT_SVELTE_MODULE_INFIX } from './constants.js';
6+
import {
7+
DEFAULT_SVELTE_MODULE_EXT,
8+
DEFAULT_SVELTE_MODULE_INFIX,
9+
SVELTE_VIRTUAL_STYLE_ID_REGEX
10+
} from './constants.js';
711

812
const VITE_FS_PREFIX = '/@fs/';
913
const IS_WINDOWS = process.platform === 'win32';
@@ -184,6 +188,53 @@ function buildModuleFilter(include, exclude, infixes, extensions) {
184188
};
185189
}
186190

191+
/**
192+
* @template T
193+
* @param {(undefined|T|Array<T>)} x
194+
* @returns {Array<T>}
195+
*/
196+
function asArray(x) {
197+
if (x == null) {
198+
return [];
199+
}
200+
return Array.isArray(x) ? x : [x];
201+
}
202+
203+
/**
204+
*
205+
* @param {string} s
206+
* @returns {string}
207+
*/
208+
function escapeRE(s) {
209+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
210+
}
211+
212+
/**
213+
* @param {import('../types/options.d.ts').ResolvedOptions} options
214+
* @returns {import('../types/id.d.ts').IdFilter}
215+
*/
216+
export function buildIdFilter(options) {
217+
const { include, exclude, extensions } = options;
218+
const extensionsRE = extensions
219+
? new RegExp(
220+
`\\.(?:${extensions
221+
.map((e) => (e.startsWith('.') ? e.slice(1) : e))
222+
.map(escapeRE)
223+
.join('|')})$`
224+
)
225+
: /\.svelte$/;
226+
return {
227+
id: {
228+
include: [
229+
extensionsRE,
230+
SVELTE_VIRTUAL_STYLE_ID_REGEX,
231+
.../**@type {Array<string|RegExp>}*/ asArray(include)
232+
],
233+
exclude: /**@type {Array<string|RegExp>}*/ asArray(exclude)
234+
}
235+
};
236+
}
237+
187238
/**
188239
* @param {import('../types/options.d.ts').ResolvedOptions} options
189240
* @returns {import('../types/id.d.ts').IdParser}

packages/vite-plugin-svelte/types/index.d.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ declare module '@sveltejs/vite-plugin-svelte' {
2222
*
2323
* @see https://github.com/micromatch/picomatch
2424
*/
25-
include?: Arrayable<string>;
25+
include?: Arrayable<string | RegExp>;
2626
/**
2727
* A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the
2828
* plugin. By default, no files are ignored.
2929
*
3030
* @see https://github.com/micromatch/picomatch
3131
*/
32-
exclude?: Arrayable<string>;
32+
exclude?: Arrayable<string | RegExp>;
3333
/**
3434
* Emit Svelte styles as virtual CSS files for Vite and other plugins to process
3535
*
@@ -187,8 +187,8 @@ declare module '@sveltejs/vite-plugin-svelte' {
187187
* @default ['.ts','.js']
188188
*/
189189
extensions?: string[];
190-
include?: Arrayable<string>;
191-
exclude?: Arrayable<string>;
190+
include?: Arrayable<string | RegExp>;
191+
exclude?: Arrayable<string | RegExp>;
192192
}
193193

194194
type Arrayable<T> = T | T[];

packages/vite-plugin-svelte/types/index.d.ts.map

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,6 @@
2626
null,
2727
null
2828
],
29-
"mappings": ";;;;aAIYA,OAAOA;;WAETC,mBAAmBA;;;;;;;;;;;kBAWZC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgGbC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDnBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;;;;;;;;;;;;MAezBC,SAASA;;kBAEGC,qBAAqBA;;;;;;;;;;;;;iBCxKtBC,MAAMA;iBCXNC,cAAcA;iBCgBRC,gBAAgBA",
29+
"mappings": ";;;;aAIYA,OAAOA;;WAETC,mBAAmBA;;;;;;;;;;;kBAWZC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgGbC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDnBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;;;;;;;;;;;;MAezBC,SAASA;;kBAEGC,qBAAqBA;;;;;;;;;;;;;iBCvKtBC,MAAMA;iBCZNC,cAAcA;iBCgBRC,gBAAgBA",
3030
"ignoreList": []
3131
}

0 commit comments

Comments
 (0)