From b1dd3ca8110737b0f8841ae9847eb9194d444857 Mon Sep 17 00:00:00 2001 From: dominikg Date: Tue, 3 Jun 2025 21:22:03 +0200 Subject: [PATCH 1/5] refactor: add object hook filters to main vite plugin --- .changeset/tangy-cars-dress.md | 8 + packages/vite-plugin-svelte/src/index.js | 164 +++++++++--------- packages/vite-plugin-svelte/src/public.d.ts | 8 +- packages/vite-plugin-svelte/src/types/id.d.ts | 7 + .../vite-plugin-svelte/src/utils/constants.js | 5 + packages/vite-plugin-svelte/src/utils/id.js | 53 +++++- packages/vite-plugin-svelte/types/index.d.ts | 8 +- .../vite-plugin-svelte/types/index.d.ts.map | 2 +- 8 files changed, 165 insertions(+), 90 deletions(-) create mode 100644 .changeset/tangy-cars-dress.md diff --git a/.changeset/tangy-cars-dress.md b/.changeset/tangy-cars-dress.md new file mode 100644 index 000000000..67220ccc6 --- /dev/null +++ b/.changeset/tangy-cars-dress.md @@ -0,0 +1,8 @@ +--- +'@sveltejs/vite-plugin-svelte': major +--- + +define filters using object hook syntax and optimize the filter for resolveId + +> [!NOTE] +> include logic has changed to files matching `svelteConfig.include` **OR** `svelteConfig.extensions`. Previously only files matching both were loaded and transformed. diff --git a/packages/vite-plugin-svelte/src/index.js b/packages/vite-plugin-svelte/src/index.js index aed46b80c..2c69a11c2 100644 --- a/packages/vite-plugin-svelte/src/index.js +++ b/packages/vite-plugin-svelte/src/index.js @@ -4,7 +4,7 @@ import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector'; import { handleHotUpdate } from './handle-hot-update.js'; import { log, logCompilerWarnings } from './utils/log.js'; import { createCompileSvelte } from './utils/compile.js'; -import { buildIdParser, buildModuleIdParser } from './utils/id.js'; +import { buildIdFilter, buildIdParser, buildModuleIdParser } from './utils/id.js'; import { buildExtraViteConfig, validateInlineOptions, @@ -20,6 +20,7 @@ import { saveSvelteMetadata } from './utils/optimizer.js'; import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache.js'; import { loadRaw } from './utils/load-raw.js'; import * as svelteCompiler from 'svelte/compiler'; +import { SVELTE_VIRTUAL_STYLE_ID_REGEX } from './utils/constants.js'; /** * @param {Partial} [inlineOptions] @@ -42,8 +43,13 @@ export function svelte(inlineOptions) { let viteConfig; /** @type {import('./types/compile.d.ts').CompileSvelte} */ let compileSvelte; + + /** @type {import('./types/id.d.ts').IdFilter} */ + let filter = { id: { include: [], exclude: [] } }; // set with correct values in configResolved + /** @type {import('./types/plugin-api.d.ts').PluginAPI} */ const api = {}; + /** @type {import('vite').Plugin[]} */ const plugins = [ { @@ -79,6 +85,7 @@ export function svelte(inlineOptions) { async configResolved(config) { options = resolveOptions(options, config, cache); patchResolvedViteConfig(config, options); + filter = buildIdFilter(options); requestParser = buildIdParser(options); compileSvelte = createCompileSvelte(); viteConfig = config; @@ -102,98 +109,95 @@ export function svelte(inlineOptions) { setupWatchers(options, cache, requestParser); }, - async load(id, opts) { - const ssr = !!opts?.ssr; - const svelteRequest = requestParser(id, !!ssr); - if (svelteRequest) { - const { filename, query, raw } = svelteRequest; - if (raw) { - const code = await loadRaw(svelteRequest, compileSvelte, options); - // prevent vite from injecting sourcemaps in the results. - return { - code, - map: { - mappings: '' - } - }; - } else { - if (query.svelte && query.type === 'style') { - const cachedCss = cache.getCSS(svelteRequest); - if (cachedCss) { - const { hasGlobal, ...css } = cachedCss; - if (hasGlobal === false) { - // hasGlobal was added in svelte 5.26.0, so make sure it is boolean false - css.meta ??= {}; - css.meta.vite ??= {}; - css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default']; + load: { + filter, + async handler(id, opts) { + const ssr = !!opts?.ssr; + const svelteRequest = requestParser(id, !!ssr); + if (svelteRequest) { + const { filename, query, raw } = svelteRequest; + if (raw) { + const code = await loadRaw(svelteRequest, compileSvelte, options); + // prevent vite from injecting sourcemaps in the results. + return { + code, + map: { + mappings: '' + } + }; + } else { + if (query.svelte && query.type === 'style') { + const cachedCss = cache.getCSS(svelteRequest); + if (cachedCss) { + const { hasGlobal, ...css } = cachedCss; + if (hasGlobal === false) { + // hasGlobal was added in svelte 5.26.0, so make sure it is boolean false + css.meta ??= {}; + css.meta.vite ??= {}; + css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default']; + } + return css; } - return css; } - } - // prevent vite asset plugin from loading files as url that should be compiled in transform - if (viteConfig.assetsInclude(filename)) { - log.debug(`load returns raw content for ${filename}`, undefined, 'load'); - return fs.readFileSync(filename, 'utf-8'); + // prevent vite asset plugin from loading files as url that should be compiled in transform + if (viteConfig.assetsInclude(filename)) { + log.debug(`load returns raw content for ${filename}`, undefined, 'load'); + return fs.readFileSync(filename, 'utf-8'); + } } } } }, - async resolveId(importee, importer, opts) { - const ssr = !!opts?.ssr; - const svelteRequest = requestParser(importee, ssr); - if (svelteRequest?.query.svelte) { - if ( - svelteRequest.query.type === 'style' && - !svelteRequest.raw && - !svelteRequest.query.inline - ) { - // return cssId with root prefix so postcss pipeline of vite finds the directory correctly - // see https://github.com/sveltejs/vite-plugin-svelte/issues/14 - log.debug( - `resolveId resolved virtual css module ${svelteRequest.cssId}`, - undefined, - 'resolve' - ); - return svelteRequest.cssId; - } + resolveId: { + // we don't use our generic filter here but a reduced one that only matches our virtual css + filter: { id: SVELTE_VIRTUAL_STYLE_ID_REGEX }, + handler(id) { + // return cssId with root prefix so postcss pipeline of vite finds the directory correctly + // see https://github.com/sveltejs/vite-plugin-svelte/issues/14 + log.debug(`resolveId resolved virtual css module ${id}`, undefined, 'resolve'); + // TODO: do we have to repeat the dance for constructing the virtual id here? our transform added it that way + return id; } }, - async transform(code, id, opts) { - const ssr = !!opts?.ssr; - const svelteRequest = requestParser(id, ssr); - if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) { - return; - } - let compileData; - try { - compileData = await compileSvelte(svelteRequest, code, options); - } catch (e) { - cache.setError(svelteRequest, e); - throw toRollupError(e, options); - } - logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options); - cache.update(compileData); - if (compileData.dependencies?.length) { - if (options.server) { - for (const dep of compileData.dependencies) { - ensureWatchedFile(options.server.watcher, dep, options.root); - } - } else if (options.isBuild && viteConfig.build.watch) { - for (const dep of compileData.dependencies) { - this.addWatchFile(dep); - } + transform: { + filter, + async handler(code, id, opts) { + const ssr = !!opts?.ssr; + const svelteRequest = requestParser(id, ssr); + if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) { + return; } - } - return { - ...compileData.compiled.js, - meta: { - vite: { - lang: compileData.lang + let compileData; + try { + compileData = await compileSvelte(svelteRequest, code, options); + } catch (e) { + cache.setError(svelteRequest, e); + throw toRollupError(e, options); + } + logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options); + cache.update(compileData); + if (compileData.dependencies?.length) { + if (options.server) { + for (const dep of compileData.dependencies) { + ensureWatchedFile(options.server.watcher, dep, options.root); + } + } else if (options.isBuild && viteConfig.build.watch) { + for (const dep of compileData.dependencies) { + this.addWatchFile(dep); + } } } - }; + return { + ...compileData.compiled.js, + meta: { + vite: { + lang: compileData.lang + } + } + }; + } }, handleHotUpdate(ctx) { diff --git a/packages/vite-plugin-svelte/src/public.d.ts b/packages/vite-plugin-svelte/src/public.d.ts index 6335e1aa7..3ac18b8ce 100644 --- a/packages/vite-plugin-svelte/src/public.d.ts +++ b/packages/vite-plugin-svelte/src/public.d.ts @@ -22,14 +22,14 @@ export interface PluginOptions { * * @see https://github.com/micromatch/picomatch */ - include?: Arrayable; + include?: Arrayable; /** * A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the * plugin. By default, no files are ignored. * * @see https://github.com/micromatch/picomatch */ - exclude?: Arrayable; + exclude?: Arrayable; /** * Emit Svelte styles as virtual CSS files for Vite and other plugins to process * @@ -187,8 +187,8 @@ interface CompileModuleOptions { * @default ['.ts','.js'] */ extensions?: string[]; - include?: Arrayable; - exclude?: Arrayable; + include?: Arrayable; + exclude?: Arrayable; } type Arrayable = T | T[]; diff --git a/packages/vite-plugin-svelte/src/types/id.d.ts b/packages/vite-plugin-svelte/src/types/id.d.ts index bbd4dd468..cbd0d3ced 100644 --- a/packages/vite-plugin-svelte/src/types/id.d.ts +++ b/packages/vite-plugin-svelte/src/types/id.d.ts @@ -39,6 +39,13 @@ export interface SvelteModuleRequest { } export type IdParser = (id: string, ssr: boolean, timestamp?: number) => SvelteRequest | undefined; + +export type IdFilter = { + id: { + include: Array; + exclude: Array; + }; +}; export type ModuleIdParser = ( id: string, ssr: boolean, diff --git a/packages/vite-plugin-svelte/src/utils/constants.js b/packages/vite-plugin-svelte/src/utils/constants.js index a69fe5589..20cfc1a3e 100644 --- a/packages/vite-plugin-svelte/src/utils/constants.js +++ b/packages/vite-plugin-svelte/src/utils/constants.js @@ -29,3 +29,8 @@ export const FAQ_LINK_MISSING_EXPORTS_CONDITION = export const DEFAULT_SVELTE_EXT = ['.svelte']; export const DEFAULT_SVELTE_MODULE_INFIX = ['.svelte.']; export const DEFAULT_SVELTE_MODULE_EXT = ['.js', '.ts']; + +export const SVELTE_VIRTUAL_STYLE_SUFFIX = '?svelte&type=style&lang.css'; +export const SVELTE_VIRTUAL_STYLE_ID_REGEX = new RegExp( + `${SVELTE_VIRTUAL_STYLE_SUFFIX.replace(/[?.]/g, '\\$&')}$` +); diff --git a/packages/vite-plugin-svelte/src/utils/id.js b/packages/vite-plugin-svelte/src/utils/id.js index cb420dd72..9a692f70c 100644 --- a/packages/vite-plugin-svelte/src/utils/id.js +++ b/packages/vite-plugin-svelte/src/utils/id.js @@ -3,7 +3,11 @@ import fs from 'node:fs'; import path from 'node:path'; import process from 'node:process'; import { log } from './log.js'; -import { DEFAULT_SVELTE_MODULE_EXT, DEFAULT_SVELTE_MODULE_INFIX } from './constants.js'; +import { + DEFAULT_SVELTE_MODULE_EXT, + DEFAULT_SVELTE_MODULE_INFIX, + SVELTE_VIRTUAL_STYLE_ID_REGEX +} from './constants.js'; const VITE_FS_PREFIX = '/@fs/'; const IS_WINDOWS = process.platform === 'win32'; @@ -184,6 +188,53 @@ function buildModuleFilter(include, exclude, infixes, extensions) { }; } +/** + * @template T + * @param {(undefined|T|Array)} x + * @returns {Array} + */ +function asArray(x) { + if (x == null) { + return []; + } + return Array.isArray(x) ? x : [x]; +} + +/** + * + * @param {string} s + * @returns {string} + */ +function escapeRE(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * @param {import('../types/options.d.ts').ResolvedOptions} options + * @returns {import('../types/id.d.ts').IdFilter} + */ +export function buildIdFilter(options) { + const { include, exclude, extensions } = options; + const extensionsRE = extensions + ? new RegExp( + `\\.(?:${extensions + .map((e) => (e.startsWith('.') ? e.slice(1) : e)) + .map(escapeRE) + .join('|')})$` + ) + : /\.svelte$/; + return { + id: { + include: [ + extensionsRE, + SVELTE_VIRTUAL_STYLE_ID_REGEX, + .../**@type {Array}*/ asArray(include) + ], + exclude: /**@type {Array}*/ asArray(exclude) + } + }; +} + /** * @param {import('../types/options.d.ts').ResolvedOptions} options * @returns {import('../types/id.d.ts').IdParser} diff --git a/packages/vite-plugin-svelte/types/index.d.ts b/packages/vite-plugin-svelte/types/index.d.ts index 1eff83d6b..aa741b9dd 100644 --- a/packages/vite-plugin-svelte/types/index.d.ts +++ b/packages/vite-plugin-svelte/types/index.d.ts @@ -22,14 +22,14 @@ declare module '@sveltejs/vite-plugin-svelte' { * * @see https://github.com/micromatch/picomatch */ - include?: Arrayable; + include?: Arrayable; /** * A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the * plugin. By default, no files are ignored. * * @see https://github.com/micromatch/picomatch */ - exclude?: Arrayable; + exclude?: Arrayable; /** * Emit Svelte styles as virtual CSS files for Vite and other plugins to process * @@ -187,8 +187,8 @@ declare module '@sveltejs/vite-plugin-svelte' { * @default ['.ts','.js'] */ extensions?: string[]; - include?: Arrayable; - exclude?: Arrayable; + include?: Arrayable; + exclude?: Arrayable; } type Arrayable = T | T[]; diff --git a/packages/vite-plugin-svelte/types/index.d.ts.map b/packages/vite-plugin-svelte/types/index.d.ts.map index db4e74717..4d0e238d1 100644 --- a/packages/vite-plugin-svelte/types/index.d.ts.map +++ b/packages/vite-plugin-svelte/types/index.d.ts.map @@ -26,6 +26,6 @@ null, null ], - "mappings": ";;;;aAIYA,OAAOA;;WAETC,mBAAmBA;;;;;;;;;;;kBAWZC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgGbC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDnBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;;;;;;;;;;;;MAezBC,SAASA;;kBAEGC,qBAAqBA;;;;;;;;;;;;;iBCxKtBC,MAAMA;iBCXNC,cAAcA;iBCgBRC,gBAAgBA", + "mappings": ";;;;aAIYA,OAAOA;;WAETC,mBAAmBA;;;;;;;;;;;kBAWZC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgGbC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDnBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;;;;;;;;;;;;MAezBC,SAASA;;kBAEGC,qBAAqBA;;;;;;;;;;;;;iBCvKtBC,MAAMA;iBCZNC,cAAcA;iBCgBRC,gBAAgBA", "ignoreList": [] } \ No newline at end of file From 3a7e7f4b7e29878abbe5c757d5f250a1c9424f44 Mon Sep 17 00:00:00 2001 From: dominikg Date: Wed, 4 Jun 2025 15:42:13 +0200 Subject: [PATCH 2/5] refactor: add object hook filter to compile module plugin --- packages/vite-plugin-svelte/src/index.js | 46 ++++++++++++------- packages/vite-plugin-svelte/src/utils/id.js | 42 +++++++++++++---- .../vite-plugin-svelte/types/index.d.ts.map | 2 +- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/packages/vite-plugin-svelte/src/index.js b/packages/vite-plugin-svelte/src/index.js index 2c69a11c2..f3febad69 100644 --- a/packages/vite-plugin-svelte/src/index.js +++ b/packages/vite-plugin-svelte/src/index.js @@ -4,7 +4,12 @@ import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector'; import { handleHotUpdate } from './handle-hot-update.js'; import { log, logCompilerWarnings } from './utils/log.js'; import { createCompileSvelte } from './utils/compile.js'; -import { buildIdFilter, buildIdParser, buildModuleIdParser } from './utils/id.js'; +import { + buildIdFilter, + buildIdParser, + buildModuleIdFilter, + buildModuleIdParser +} from './utils/id.js'; import { buildExtraViteConfig, validateInlineOptions, @@ -47,6 +52,9 @@ export function svelte(inlineOptions) { /** @type {import('./types/id.d.ts').IdFilter} */ let filter = { id: { include: [], exclude: [] } }; // set with correct values in configResolved + /** @type {import('./types/id.d.ts').IdFilter} */ + let moduleFilter = { id: { include: [], exclude: [] } }; // set with correct values in configResolved + /** @type {import('./types/plugin-api.d.ts').PluginAPI} */ const api = {}; @@ -217,24 +225,28 @@ export function svelte(inlineOptions) { name: 'vite-plugin-svelte-module', enforce: 'post', async configResolved() { + moduleFilter = buildModuleIdFilter(options); moduleRequestParser = buildModuleIdParser(options); }, - async transform(code, id, opts) { - const ssr = !!opts?.ssr; - const moduleRequest = moduleRequestParser(id, ssr); - if (!moduleRequest) { - return; - } - try { - const compileResult = svelteCompiler.compileModule(code, { - dev: !viteConfig.isProduction, - generate: ssr ? 'server' : 'client', - filename: moduleRequest.filename - }); - logCompilerWarnings(moduleRequest, compileResult.warnings, options); - return compileResult.js; - } catch (e) { - throw toRollupError(e, options); + transform: { + filter: moduleFilter, + async handler(code, id, opts) { + const ssr = !!opts?.ssr; + const moduleRequest = moduleRequestParser(id, ssr); + if (!moduleRequest) { + return; + } + try { + const compileResult = svelteCompiler.compileModule(code, { + dev: !viteConfig.isProduction, + generate: ssr ? 'server' : 'client', + filename: moduleRequest.filename + }); + logCompilerWarnings(moduleRequest, compileResult.warnings, options); + return compileResult.js; + } catch (e) { + throw toRollupError(e, options); + } } } }, diff --git a/packages/vite-plugin-svelte/src/utils/id.js b/packages/vite-plugin-svelte/src/utils/id.js index 9a692f70c..c01c10d61 100644 --- a/packages/vite-plugin-svelte/src/utils/id.js +++ b/packages/vite-plugin-svelte/src/utils/id.js @@ -4,6 +4,7 @@ import path from 'node:path'; import process from 'node:process'; import { log } from './log.js'; import { + DEFAULT_SVELTE_EXT, DEFAULT_SVELTE_MODULE_EXT, DEFAULT_SVELTE_MODULE_INFIX, SVELTE_VIRTUAL_STYLE_ID_REGEX @@ -214,15 +215,13 @@ function escapeRE(s) { * @returns {import('../types/id.d.ts').IdFilter} */ export function buildIdFilter(options) { - const { include, exclude, extensions } = options; - const extensionsRE = extensions - ? new RegExp( - `\\.(?:${extensions - .map((e) => (e.startsWith('.') ? e.slice(1) : e)) - .map(escapeRE) - .join('|')})$` - ) - : /\.svelte$/; + const { include = [], exclude = [], extensions = DEFAULT_SVELTE_EXT } = options; + const extensionsRE = new RegExp( + `\\.(?:${extensions + .map((e) => (e.startsWith('.') ? e.slice(1) : e)) + .map(escapeRE) + .join('|')})$` + ); return { id: { include: [ @@ -251,6 +250,31 @@ export function buildIdParser(options) { }; } +/** + * @param {import('../types/options.d.ts').ResolvedOptions} options + * @returns {import('../types/id.d.ts').IdFilter} + */ +export function buildModuleIdFilter(options) { + const { + infixes = DEFAULT_SVELTE_MODULE_INFIX, + include = [], + exclude = [], + extensions = DEFAULT_SVELTE_MODULE_EXT + } = options.experimental?.compileModule ?? {}; + const infixWithExtRE = new RegExp( + `(?:${infixes.map(escapeRE).join('|')})(?:[^.\\\\/]+\\.)*(?:${extensions + .map((e) => (e.startsWith('.') ? e.slice(1) : e)) + .map(escapeRE) + .join('|')})$` + ); + return { + id: { + include: [infixWithExtRE, .../**@type {Array}*/ asArray(include)], + exclude: /**@type {Array}*/ asArray(exclude) + } + }; +} + /** * @param {import('../types/options.d.ts').ResolvedOptions} options * @returns {import('../types/id.d.ts').ModuleIdParser} diff --git a/packages/vite-plugin-svelte/types/index.d.ts.map b/packages/vite-plugin-svelte/types/index.d.ts.map index 4d0e238d1..4f996bccb 100644 --- a/packages/vite-plugin-svelte/types/index.d.ts.map +++ b/packages/vite-plugin-svelte/types/index.d.ts.map @@ -26,6 +26,6 @@ null, null ], - "mappings": ";;;;aAIYA,OAAOA;;WAETC,mBAAmBA;;;;;;;;;;;kBAWZC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgGbC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDnBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;;;;;;;;;;;;MAezBC,SAASA;;kBAEGC,qBAAqBA;;;;;;;;;;;;;iBCvKtBC,MAAMA;iBCZNC,cAAcA;iBCgBRC,gBAAgBA", + "mappings": ";;;;aAIYA,OAAOA;;WAETC,mBAAmBA;;;;;;;;;;;kBAWZC,aAAaA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAgGbC,YAAYA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAiDnBC,mBAAmBA;;;;;;;;;;;;;;;;WAgBnBC,oBAAoBA;;;;;;;;;;;;;;;MAezBC,SAASA;;kBAEGC,qBAAqBA;;;;;;;;;;;;;iBClKtBC,MAAMA;iBCjBNC,cAAcA;iBCgBRC,gBAAgBA", "ignoreList": [] } \ No newline at end of file From 6abecb23bc28dfc9fc2e8fe76e870406ed2961a7 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 5 Jun 2025 17:09:19 +0200 Subject: [PATCH 3/5] fix filters and add unit test --- .../vite-plugin-svelte/__tests__/id.spec.js | 39 ++ packages/vite-plugin-svelte/src/index.js | 361 +++++++++--------- packages/vite-plugin-svelte/src/utils/id.js | 93 +---- .../vite-plugin-svelte/src/utils/options.js | 6 +- 4 files changed, 239 insertions(+), 260 deletions(-) create mode 100644 packages/vite-plugin-svelte/__tests__/id.spec.js diff --git a/packages/vite-plugin-svelte/__tests__/id.spec.js b/packages/vite-plugin-svelte/__tests__/id.spec.js new file mode 100644 index 000000000..f2ce9d052 --- /dev/null +++ b/packages/vite-plugin-svelte/__tests__/id.spec.js @@ -0,0 +1,39 @@ +import { describe, it, expect } from 'vitest'; +import { buildIdFilter, buildModuleIdFilter } from '../src/utils/id.js'; + +function passes(filter, id) { + const included = filter.id.include.some((includeRE) => includeRE.test(id)); + return included && !filter.id.exclude.some((excludeRE) => excludeRE.test(id)); +} + +describe('buildIdFilter', () => { + it('default filter matches .svelte files', () => { + const filter = buildIdFilter({}); + expect(passes(filter, '/src/Foo.svelte')).toBe(true); + expect(passes(filter, '/src/Foo.svelte?something')).toBe(true); + }); + + it('custom filter matches .svx files', () => { + const filter = buildIdFilter({ extensions: ['.svelte', '.svx'] }); + expect(passes(filter, '/src/Foo.svx')).toBe(true); + expect(passes(filter, '/src/Foo.svx?something')).toBe(true); + }); +}); + +describe('buildModuleIdFilter', () => { + it('default filter matches .svelte. files', () => { + const filter = buildModuleIdFilter({}); + expect(passes(filter, '/src/foo.svelte.js')).toBe(true); + expect(passes(filter, '/src/foo.svelte.ts')).toBe(true); + expect(passes(filter, '/src/foo.svelte.test.js')).toBe(true); + expect(passes(filter, '/src/foo.svelte.test.ts')).toBe(true); + }); + + it('custom filter matches .svx. files', () => { + const filter = buildModuleIdFilter({ experimental: { compileModule: { infixes: ['.svx.'] } } }); + expect(passes(filter, '/src/foo.svx.js')).toBe(true); + expect(passes(filter, '/src/foo.svx.ts')).toBe(true); + expect(passes(filter, '/src/foo.svx.test.js')).toBe(true); + expect(passes(filter, '/src/foo.svx.test.ts')).toBe(true); + }); +}); diff --git a/packages/vite-plugin-svelte/src/index.js b/packages/vite-plugin-svelte/src/index.js index f3febad69..a4a87aef4 100644 --- a/packages/vite-plugin-svelte/src/index.js +++ b/packages/vite-plugin-svelte/src/index.js @@ -49,209 +49,208 @@ export function svelte(inlineOptions) { /** @type {import('./types/compile.d.ts').CompileSvelte} */ let compileSvelte; - /** @type {import('./types/id.d.ts').IdFilter} */ - let filter = { id: { include: [], exclude: [] } }; // set with correct values in configResolved - - /** @type {import('./types/id.d.ts').IdFilter} */ - let moduleFilter = { id: { include: [], exclude: [] } }; // set with correct values in configResolved + /** @type {import('vite').Plugin} */ + const compilePlugin = { + name: 'vite-plugin-svelte', + // make sure our resolver runs before vite internal resolver to resolve svelte field correctly + enforce: 'pre', + /** @type {import('./types/plugin-api.d.ts').PluginAPI} */ + api: {}, + async config(config, configEnv) { + // setup logger + if (process.env.DEBUG) { + log.setLevel('debug'); + } else if (config.logLevel) { + log.setLevel(config.logLevel); + } + // @ts-expect-error temporarily lend the options variable until fixed in configResolved + options = await preResolveOptions(inlineOptions, config, configEnv); + // extra vite config + const extraViteConfig = await buildExtraViteConfig(options, config); + log.debug('additional vite config', extraViteConfig, 'config'); + return extraViteConfig; + }, - /** @type {import('./types/plugin-api.d.ts').PluginAPI} */ - const api = {}; + configEnvironment(name, config, opts) { + ensureConfigEnvironmentMainFields(name, config, opts); + // @ts-expect-error the function above should make `resolve.mainFields` non-nullable + config.resolve.mainFields.unshift('svelte'); - /** @type {import('vite').Plugin[]} */ - const plugins = [ - { - name: 'vite-plugin-svelte', - // make sure our resolver runs before vite internal resolver to resolve svelte field correctly - enforce: 'pre', - api, - async config(config, configEnv) { - // setup logger - if (process.env.DEBUG) { - log.setLevel('debug'); - } else if (config.logLevel) { - log.setLevel(config.logLevel); - } - // @ts-expect-error temporarily lend the options variable until fixed in configResolved - options = await preResolveOptions(inlineOptions, config, configEnv); - // extra vite config - const extraViteConfig = await buildExtraViteConfig(options, config); - log.debug('additional vite config', extraViteConfig, 'config'); - return extraViteConfig; - }, - - configEnvironment(name, config, opts) { - ensureConfigEnvironmentMainFields(name, config, opts); - // @ts-expect-error the function above should make `resolve.mainFields` non-nullable - config.resolve.mainFields.unshift('svelte'); + ensureConfigEnvironmentConditions(name, config, opts); + // @ts-expect-error the function above should make `resolve.conditions` non-nullable + config.resolve.conditions.push('svelte'); + }, - ensureConfigEnvironmentConditions(name, config, opts); - // @ts-expect-error the function above should make `resolve.conditions` non-nullable - config.resolve.conditions.push('svelte'); - }, + async configResolved(config) { + options = resolveOptions(options, config, cache); + patchResolvedViteConfig(config, options); + const filter = buildIdFilter(options); + //@ts-expect-error transform defined below but filter not in type + compilePlugin.transform.filter = filter; + //@ts-expect-error load defined below but filter not in type + compilePlugin.load.filter = filter; - async configResolved(config) { - options = resolveOptions(options, config, cache); - patchResolvedViteConfig(config, options); - filter = buildIdFilter(options); - requestParser = buildIdParser(options); - compileSvelte = createCompileSvelte(); - viteConfig = config; - // TODO deep clone to avoid mutability from outside? - api.options = options; - log.debug('resolved options', options, 'config'); - }, + requestParser = buildIdParser(options); + compileSvelte = createCompileSvelte(); + viteConfig = config; + // TODO deep clone to avoid mutability from outside? + compilePlugin.api.options = options; + log.debug('resolved options', options, 'config'); + log.debug('filters', filter, 'config'); + }, - async buildStart() { - if (!options.prebundleSvelteLibraries) return; - const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options); - if (isSvelteMetadataChanged) { - // Force Vite to optimize again. Although we mutate the config here, it works because - // Vite's optimizer runs after `buildStart()`. - viteConfig.optimizeDeps.force = true; - } - }, + async buildStart() { + if (!options.prebundleSvelteLibraries) return; + const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options); + if (isSvelteMetadataChanged) { + // Force Vite to optimize again. Although we mutate the config here, it works because + // Vite's optimizer runs after `buildStart()`. + viteConfig.optimizeDeps.force = true; + } + }, - configureServer(server) { - options.server = server; - setupWatchers(options, cache, requestParser); - }, + configureServer(server) { + options.server = server; + setupWatchers(options, cache, requestParser); + }, - load: { - filter, - async handler(id, opts) { - const ssr = !!opts?.ssr; - const svelteRequest = requestParser(id, !!ssr); - if (svelteRequest) { - const { filename, query, raw } = svelteRequest; - if (raw) { - const code = await loadRaw(svelteRequest, compileSvelte, options); - // prevent vite from injecting sourcemaps in the results. - return { - code, - map: { - mappings: '' - } - }; - } else { - if (query.svelte && query.type === 'style') { - const cachedCss = cache.getCSS(svelteRequest); - if (cachedCss) { - const { hasGlobal, ...css } = cachedCss; - if (hasGlobal === false) { - // hasGlobal was added in svelte 5.26.0, so make sure it is boolean false - css.meta ??= {}; - css.meta.vite ??= {}; - css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default']; - } - return css; - } + load: { + async handler(id, opts) { + const ssr = !!opts?.ssr; + const svelteRequest = requestParser(id, !!ssr); + if (svelteRequest) { + const { filename, query, raw } = svelteRequest; + if (raw) { + const code = await loadRaw(svelteRequest, compileSvelte, options); + // prevent vite from injecting sourcemaps in the results. + return { + code, + map: { + mappings: '' } - // prevent vite asset plugin from loading files as url that should be compiled in transform - if (viteConfig.assetsInclude(filename)) { - log.debug(`load returns raw content for ${filename}`, undefined, 'load'); - return fs.readFileSync(filename, 'utf-8'); + }; + } else { + if (query.svelte && query.type === 'style') { + const cachedCss = cache.getCSS(svelteRequest); + if (cachedCss) { + const { hasGlobal, ...css } = cachedCss; + if (hasGlobal === false) { + // hasGlobal was added in svelte 5.26.0, so make sure it is boolean false + css.meta ??= {}; + css.meta.vite ??= {}; + css.meta.vite.cssScopeTo = [svelteRequest.filename, 'default']; + } + return css; } } + // prevent vite asset plugin from loading files as url that should be compiled in transform + if (viteConfig.assetsInclude(filename)) { + log.debug(`load returns raw content for ${filename}`, undefined, 'load'); + return fs.readFileSync(filename, 'utf-8'); + } } } - }, + } + }, - resolveId: { - // we don't use our generic filter here but a reduced one that only matches our virtual css - filter: { id: SVELTE_VIRTUAL_STYLE_ID_REGEX }, - handler(id) { - // return cssId with root prefix so postcss pipeline of vite finds the directory correctly - // see https://github.com/sveltejs/vite-plugin-svelte/issues/14 - log.debug(`resolveId resolved virtual css module ${id}`, undefined, 'resolve'); - // TODO: do we have to repeat the dance for constructing the virtual id here? our transform added it that way - return id; - } - }, + resolveId: { + // we don't use our generic filter here but a reduced one that only matches our virtual css + filter: { id: SVELTE_VIRTUAL_STYLE_ID_REGEX }, + handler(id) { + // return cssId with root prefix so postcss pipeline of vite finds the directory correctly + // see https://github.com/sveltejs/vite-plugin-svelte/issues/14 + log.debug(`resolveId resolved virtual css module ${id}`, undefined, 'resolve'); + // TODO: do we have to repeat the dance for constructing the virtual id here? our transform added it that way + return id; + } + }, - transform: { - filter, - async handler(code, id, opts) { - const ssr = !!opts?.ssr; - const svelteRequest = requestParser(id, ssr); - if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) { - return; - } - let compileData; - try { - compileData = await compileSvelte(svelteRequest, code, options); - } catch (e) { - cache.setError(svelteRequest, e); - throw toRollupError(e, options); - } - logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options); - cache.update(compileData); - if (compileData.dependencies?.length) { - if (options.server) { - for (const dep of compileData.dependencies) { - ensureWatchedFile(options.server.watcher, dep, options.root); - } - } else if (options.isBuild && viteConfig.build.watch) { - for (const dep of compileData.dependencies) { - this.addWatchFile(dep); - } + transform: { + async handler(code, id, opts) { + log.debug(`transforming ${id}`, undefined, 'transform'); + const ssr = !!opts?.ssr; + const svelteRequest = requestParser(id, ssr); + if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) { + return; + } + let compileData; + try { + compileData = await compileSvelte(svelteRequest, code, options); + } catch (e) { + cache.setError(svelteRequest, e); + throw toRollupError(e, options); + } + logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options); + cache.update(compileData); + if (compileData.dependencies?.length) { + if (options.server) { + for (const dep of compileData.dependencies) { + ensureWatchedFile(options.server.watcher, dep, options.root); } - } - return { - ...compileData.compiled.js, - meta: { - vite: { - lang: compileData.lang - } + } else if (options.isBuild && viteConfig.build.watch) { + for (const dep of compileData.dependencies) { + this.addWatchFile(dep); } - }; + } } - }, + return { + ...compileData.compiled.js, + meta: { + vite: { + lang: compileData.lang + } + } + }; + } + }, - handleHotUpdate(ctx) { - if (!options.compilerOptions.hmr || !options.emitCss) { - return; - } - const svelteRequest = requestParser(ctx.file, false, ctx.timestamp); - if (svelteRequest) { - return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options); - } - }, - async buildEnd() { - await options.stats?.finishAll(); + handleHotUpdate(ctx) { + if (!options.compilerOptions.hmr || !options.emitCss) { + return; + } + const svelteRequest = requestParser(ctx.file, false, ctx.timestamp); + if (svelteRequest) { + return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options); } }, - { - name: 'vite-plugin-svelte-module', - enforce: 'post', - async configResolved() { - moduleFilter = buildModuleIdFilter(options); - moduleRequestParser = buildModuleIdParser(options); - }, - transform: { - filter: moduleFilter, - async handler(code, id, opts) { - const ssr = !!opts?.ssr; - const moduleRequest = moduleRequestParser(id, ssr); - if (!moduleRequest) { - return; - } - try { - const compileResult = svelteCompiler.compileModule(code, { - dev: !viteConfig.isProduction, - generate: ssr ? 'server' : 'client', - filename: moduleRequest.filename - }); - logCompilerWarnings(moduleRequest, compileResult.warnings, options); - return compileResult.js; - } catch (e) { - throw toRollupError(e, options); - } + async buildEnd() { + await options.stats?.finishAll(); + } + }; + + /** @type {import('vite').Plugin} */ + const moduleCompilePlugin = { + name: 'vite-plugin-svelte-module', + enforce: 'post', + async configResolved() { + //@ts-expect-error transform defined below but filter not in type + moduleCompilePlugin.transform.filter = buildModuleIdFilter(options); + moduleRequestParser = buildModuleIdParser(options); + }, + transform: { + async handler(code, id, opts) { + const ssr = !!opts?.ssr; + const moduleRequest = moduleRequestParser(id, ssr); + if (!moduleRequest) { + return; + } + try { + const compileResult = svelteCompiler.compileModule(code, { + dev: !viteConfig.isProduction, + generate: ssr ? 'server' : 'client', + filename: moduleRequest.filename + }); + logCompilerWarnings(moduleRequest, compileResult.warnings, options); + return compileResult.js; + } catch (e) { + throw toRollupError(e, options); } } - }, - svelteInspector() - ]; + } + }; + + /** @type {import('vite').Plugin[]} */ + const plugins = [compilePlugin, moduleCompilePlugin, svelteInspector()]; return plugins; } diff --git a/packages/vite-plugin-svelte/src/utils/id.js b/packages/vite-plugin-svelte/src/utils/id.js index c01c10d61..479281f50 100644 --- a/packages/vite-plugin-svelte/src/utils/id.js +++ b/packages/vite-plugin-svelte/src/utils/id.js @@ -1,14 +1,13 @@ -import { createFilter, normalizePath } from 'vite'; +import { normalizePath } from 'vite'; import fs from 'node:fs'; -import path from 'node:path'; import process from 'node:process'; import { log } from './log.js'; import { DEFAULT_SVELTE_EXT, DEFAULT_SVELTE_MODULE_EXT, - DEFAULT_SVELTE_MODULE_INFIX, - SVELTE_VIRTUAL_STYLE_ID_REGEX + DEFAULT_SVELTE_MODULE_INFIX } from './constants.js'; +import { arraify } from './options.js'; const VITE_FS_PREFIX = '/@fs/'; const IS_WINDOWS = process.platform === 'win32'; @@ -158,49 +157,6 @@ function stripRoot(normalizedFilename, normalizedRoot) { : normalizedFilename; } -/** - * @param {import('../public.d.ts').Options['include'] | undefined} include - * @param {import('../public.d.ts').Options['exclude'] | undefined} exclude - * @param {string[]} extensions - * @returns {(filename: string) => boolean} - */ -function buildFilter(include, exclude, extensions) { - const rollupFilter = createFilter(include, exclude); - return (filename) => rollupFilter(filename) && extensions.some((ext) => filename.endsWith(ext)); -} - -/** - * @param {import('../public.d.ts').Options['include'] | undefined} include - * @param {import('../public.d.ts').Options['exclude'] | undefined} exclude - * @param {string[]} infixes - * @param {string[]} extensions - * @returns {(filename: string) => boolean} - */ -function buildModuleFilter(include, exclude, infixes, extensions) { - const rollupFilter = createFilter(include, exclude); - return (filename) => { - const basename = path.basename(filename); - - return ( - rollupFilter(filename) && - infixes.some((infix) => basename.includes(infix)) && - extensions.some((ext) => basename.endsWith(ext)) - ); - }; -} - -/** - * @template T - * @param {(undefined|T|Array)} x - * @returns {Array} - */ -function asArray(x) { - if (x == null) { - return []; - } - return Array.isArray(x) ? x : [x]; -} - /** * * @param {string} s @@ -216,22 +172,20 @@ function escapeRE(s) { */ export function buildIdFilter(options) { const { include = [], exclude = [], extensions = DEFAULT_SVELTE_EXT } = options; + // the final regex looks like this const extensionsRE = new RegExp( - `\\.(?:${extensions + `^[^?#]+\\.(?:${extensions .map((e) => (e.startsWith('.') ? e.slice(1) : e)) .map(escapeRE) - .join('|')})$` + .join('|')})(?:[?#]|$)` ); - return { + const filter = { id: { - include: [ - extensionsRE, - SVELTE_VIRTUAL_STYLE_ID_REGEX, - .../**@type {Array}*/ asArray(include) - ], - exclude: /**@type {Array}*/ asArray(exclude) + include: [extensionsRE, .../**@type {Array}*/ arraify(include)], + exclude: /**@type {Array}*/ arraify(exclude) } }; + return filter; } /** @@ -239,14 +193,10 @@ export function buildIdFilter(options) { * @returns {import('../types/id.d.ts').IdParser} */ export function buildIdParser(options) { - const { include, exclude, extensions, root } = options; - const normalizedRoot = normalizePath(root); - const filter = buildFilter(include, exclude, extensions ?? []); + const normalizedRoot = normalizePath(options.root); return (id, ssr, timestamp = Date.now()) => { const { filename, rawQuery } = splitId(id); - if (filter(filename)) { - return parseToSvelteRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr); - } + return parseToSvelteRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr); }; } @@ -262,15 +212,15 @@ export function buildModuleIdFilter(options) { extensions = DEFAULT_SVELTE_MODULE_EXT } = options.experimental?.compileModule ?? {}; const infixWithExtRE = new RegExp( - `(?:${infixes.map(escapeRE).join('|')})(?:[^.\\\\/]+\\.)*(?:${extensions + `^[^?#]+(?:${infixes.map(escapeRE).join('|')})(?:[^.\\\\/]+\\.)*(?:${extensions .map((e) => (e.startsWith('.') ? e.slice(1) : e)) .map(escapeRE) - .join('|')})$` + .join('|')})(?:[?#]|$)` ); return { id: { - include: [infixWithExtRE, .../**@type {Array}*/ asArray(include)], - exclude: /**@type {Array}*/ asArray(exclude) + include: [infixWithExtRE, .../**@type {Array}*/ arraify(include)], + exclude: /**@type {Array}*/ arraify(exclude) } }; } @@ -280,20 +230,11 @@ export function buildModuleIdFilter(options) { * @returns {import('../types/id.d.ts').ModuleIdParser} */ export function buildModuleIdParser(options) { - const { - include, - exclude, - infixes = DEFAULT_SVELTE_MODULE_INFIX, - extensions = DEFAULT_SVELTE_MODULE_EXT - } = options?.experimental?.compileModule ?? {}; const root = options.root; const normalizedRoot = normalizePath(root); - const filter = buildModuleFilter(include, exclude, infixes, extensions); return (id, ssr, timestamp = Date.now()) => { const { filename, rawQuery } = splitId(id); - if (filter(filename)) { - return parseToSvelteModuleRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr); - } + return parseToSvelteModuleRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr); }; } diff --git a/packages/vite-plugin-svelte/src/utils/options.js b/packages/vite-plugin-svelte/src/utils/options.js index 1cd0f69fd..e86411da9 100644 --- a/packages/vite-plugin-svelte/src/utils/options.js +++ b/packages/vite-plugin-svelte/src/utils/options.js @@ -647,9 +647,9 @@ export function ensureConfigEnvironmentConditions(name, config, opts) { /** * @template T - * @param {T | T[]} value + * @param {T | T[] | null | undefined } value * @returns {T[]} */ -function arraify(value) { - return Array.isArray(value) ? value : [value]; +export function arraify(value) { + return value == null ? [] : Array.isArray(value) ? value : [value]; } From 58cfb70568fe5070f5035e61b789a198d3399bd3 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 5 Jun 2025 17:22:51 +0200 Subject: [PATCH 4/5] chore: cleanup --- packages/vite-plugin-svelte/src/index.js | 1 - packages/vite-plugin-svelte/src/utils/id.js | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/vite-plugin-svelte/src/index.js b/packages/vite-plugin-svelte/src/index.js index a4a87aef4..1e4462bbc 100644 --- a/packages/vite-plugin-svelte/src/index.js +++ b/packages/vite-plugin-svelte/src/index.js @@ -167,7 +167,6 @@ export function svelte(inlineOptions) { transform: { async handler(code, id, opts) { - log.debug(`transforming ${id}`, undefined, 'transform'); const ssr = !!opts?.ssr; const svelteRequest = requestParser(id, ssr); if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) { diff --git a/packages/vite-plugin-svelte/src/utils/id.js b/packages/vite-plugin-svelte/src/utils/id.js index 479281f50..9bf967673 100644 --- a/packages/vite-plugin-svelte/src/utils/id.js +++ b/packages/vite-plugin-svelte/src/utils/id.js @@ -172,7 +172,7 @@ function escapeRE(s) { */ export function buildIdFilter(options) { const { include = [], exclude = [], extensions = DEFAULT_SVELTE_EXT } = options; - // the final regex looks like this + // this regex combines configured extensions and looks for them at the end of the string or directly before first ? or # const extensionsRE = new RegExp( `^[^?#]+\\.(?:${extensions .map((e) => (e.startsWith('.') ? e.slice(1) : e)) @@ -211,6 +211,8 @@ export function buildModuleIdFilter(options) { exclude = [], extensions = DEFAULT_SVELTE_MODULE_EXT } = options.experimental?.compileModule ?? {}; + // this regex combines configured infixes and extensions + // and looks for them at the end of the string or directly before first ? or # const infixWithExtRE = new RegExp( `^[^?#]+(?:${infixes.map(escapeRE).join('|')})(?:[^.\\\\/]+\\.)*(?:${extensions .map((e) => (e.startsWith('.') ? e.slice(1) : e)) From c93cd002d1c7e55634fc6ff877ff555eb2ac3e38 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 5 Jun 2025 17:26:17 +0200 Subject: [PATCH 5/5] negative tests --- packages/vite-plugin-svelte/__tests__/id.spec.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/vite-plugin-svelte/__tests__/id.spec.js b/packages/vite-plugin-svelte/__tests__/id.spec.js index f2ce9d052..483589efb 100644 --- a/packages/vite-plugin-svelte/__tests__/id.spec.js +++ b/packages/vite-plugin-svelte/__tests__/id.spec.js @@ -13,6 +13,12 @@ describe('buildIdFilter', () => { expect(passes(filter, '/src/Foo.svelte?something')).toBe(true); }); + it('default filter does not match .js files', () => { + const filter = buildIdFilter({}); + expect(passes(filter, '/src/foo.js')).toBe(false); + expect(passes(filter, '/src/foo.js?something')).toBe(false); + }); + it('custom filter matches .svx files', () => { const filter = buildIdFilter({ extensions: ['.svelte', '.svx'] }); expect(passes(filter, '/src/Foo.svx')).toBe(true); @@ -21,7 +27,7 @@ describe('buildIdFilter', () => { }); describe('buildModuleIdFilter', () => { - it('default filter matches .svelte. files', () => { + it('default filter matches .svelte.*.js/ts files', () => { const filter = buildModuleIdFilter({}); expect(passes(filter, '/src/foo.svelte.js')).toBe(true); expect(passes(filter, '/src/foo.svelte.ts')).toBe(true); @@ -29,6 +35,14 @@ describe('buildModuleIdFilter', () => { expect(passes(filter, '/src/foo.svelte.test.ts')).toBe(true); }); + it('default filter does not match files without .svelte.', () => { + const filter = buildModuleIdFilter({}); + expect(passes(filter, '/src/foo.js')).toBe(false); + expect(passes(filter, '/src/foo.ts')).toBe(false); + expect(passes(filter, '/src/foo.test.js')).toBe(false); + expect(passes(filter, '/src/foo.test.ts')).toBe(false); + }); + it('custom filter matches .svx. files', () => { const filter = buildModuleIdFilter({ experimental: { compileModule: { infixes: ['.svx.'] } } }); expect(passes(filter, '/src/foo.svx.js')).toBe(true);