From ebe18d4931611d72047fd42015719fd6b6ed4abd Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 24 Nov 2022 12:00:56 +0100 Subject: [PATCH 01/14] feat: log deprecation warning if svelte field causes difference in resolve --- .changeset/cold-horses-begin.md | 5 ++ packages/vite-plugin-svelte/src/index.ts | 21 +++++++- .../vite-plugin-svelte/src/utils/compile.ts | 1 + packages/vite-plugin-svelte/src/utils/log.ts | 2 +- .../vite-plugin-svelte/src/utils/options.ts | 6 ++- .../src/utils/vite-plugin-svelte-cache.ts | 52 +++++++++++++++++++ .../src/utils/vite-plugin-svelte-stats.ts | 38 +++----------- 7 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 .changeset/cold-horses-begin.md diff --git a/.changeset/cold-horses-begin.md b/.changeset/cold-horses-begin.md new file mode 100644 index 000000000..de5bc2c54 --- /dev/null +++ b/.changeset/cold-horses-begin.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/vite-plugin-svelte': patch +--- + +log deprecation warnings for packages that use the "svelte" field to resolve svelte files differently than standard vite resolve diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 1ca56a534..8760c0b15 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -76,7 +76,7 @@ export function svelte(inlineOptions?: Partial): Plugin[] { }, async configResolved(config) { - options = resolveOptions(options, config); + options = resolveOptions(options, config, cache); patchResolvedViteConfig(config, options); requestParser = buildIdParser(options); compileSvelte = createCompileSvelte(options); @@ -169,9 +169,26 @@ export function svelte(inlineOptions?: Partial): Plugin[] { try { const resolved = await resolveViaPackageJsonSvelte(importee, importer, cache); if (resolved) { - log.debug( + log.debug.once( `resolveId resolved ${resolved} via package.json svelte field of ${importee}` ); + try { + const viteResolved = ( + await this.resolve(importee, importer, { ...opts, skipSelf: true }) + )?.id; + if (resolved !== viteResolved) { + const packageInfo = await cache.getPackageInfo(resolved); + log.warn.once( + `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files differently than standard vite resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.` + ); + } + } catch (e) { + const packageInfo = await cache.getPackageInfo(resolved); + log.warn.once( + `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files but standard vite resolve failed to resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.` + ); + } + return resolved; } } catch (e) { diff --git a/packages/vite-plugin-svelte/src/utils/compile.ts b/packages/vite-plugin-svelte/src/utils/compile.ts index f169c31d3..f1b346aab 100644 --- a/packages/vite-plugin-svelte/src/utils/compile.ts +++ b/packages/vite-plugin-svelte/src/utils/compile.ts @@ -113,6 +113,7 @@ const _createCompileSvelte = (makeHot: Function) => { compiled.js.code = makeHot({ id: filename, compiledCode: compiled.js.code, + // @ts-expect-error hot isn't a boolean anymore at this point hotOptions: { ...options.hot, injectCss: options.hot?.injectCss === true && hasCss }, compiled, originalCode: code, diff --git a/packages/vite-plugin-svelte/src/utils/log.ts b/packages/vite-plugin-svelte/src/utils/log.ts index 2c1e1e124..16f7a8817 100644 --- a/packages/vite-plugin-svelte/src/utils/log.ts +++ b/packages/vite-plugin-svelte/src/utils/log.ts @@ -73,7 +73,7 @@ function createLogger(level: string): LogFn { const logFn: LogFn = _log.bind(null, logger) as LogFn; const logged = new Set(); const once = function (message: string, payload?: any) { - if (logged.has(message)) { + if (!logger.enabled || logged.has(message)) { return; } logged.add(message); diff --git a/packages/vite-plugin-svelte/src/utils/options.ts b/packages/vite-plugin-svelte/src/utils/options.ts index f6a270819..1895cf560 100644 --- a/packages/vite-plugin-svelte/src/utils/options.ts +++ b/packages/vite-plugin-svelte/src/utils/options.ts @@ -33,6 +33,7 @@ import { import { atLeastSvelte } from './svelte-version'; import { isCommonDepWithoutSvelteField } from './dependencies'; import { VitePluginSvelteStats } from './vite-plugin-svelte-stats'; +import { VitePluginSvelteCache } from './vite-plugin-svelte-cache'; // svelte 3.53.0 changed compilerOptions.css from boolean to string | boolen, use string when available const cssAsString = atLeastSvelte('3.53.0'); @@ -180,7 +181,8 @@ function mergeConfigs(...configs: (Partial | undefined)[]): T { // also validates the final config. export function resolveOptions( preResolveOptions: PreResolvedOptions, - viteConfig: ResolvedConfig + viteConfig: ResolvedConfig, + cache: VitePluginSvelteCache ): ResolvedOptions { const css = cssAsString ? preResolveOptions.emitCss @@ -217,7 +219,7 @@ export function resolveOptions( const statsEnabled = disableCompileStats !== true && disableCompileStats !== (merged.isBuild ? 'build' : 'dev'); if (statsEnabled && isLogLevelInfo) { - merged.stats = new VitePluginSvelteStats(); + merged.stats = new VitePluginSvelteStats(cache); } return merged; } diff --git a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts index 167bbddea..370c09853 100644 --- a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts +++ b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts @@ -1,5 +1,17 @@ import { SvelteRequest } from './id'; import { Code, CompileData } from './compile'; +import { readFileSync } from 'fs'; +import { dirname } from 'path'; +//eslint-disable-next-line node/no-missing-import +import { findClosestPkgJsonPath } from 'vitefu'; +import { normalizePath } from 'vite'; + +interface PackageInfo { + name: string; + version: string; + svelte?: string; + path: string; +} export class VitePluginSvelteCache { private _css = new Map(); @@ -8,6 +20,7 @@ export class VitePluginSvelteCache { private _dependants = new Map>(); private _resolvedSvelteFields = new Map(); private _errors = new Map(); + private _packageInfos: PackageInfo[] = []; public update(compileData: CompileData) { this._errors.delete(compileData.normalizedFilename); @@ -124,4 +137,43 @@ export class VitePluginSvelteCache { private _getResolvedSvelteFieldKey(importee: string, importer?: string): string { return importer ? `${importer} > ${importee}` : importee; } + + public async getPackageInfo(file: string): Promise { + let info = this._packageInfos.find((pi) => file.startsWith(pi.path)); + if (!info) { + info = await findPackageInfo(file); + this._packageInfos.push(info); + } + return info; + } +} + +/** + * utility to get some info from the package.json a file belongs to (closest one with a "name" set) + * + * @param {string} file to find info for + * @returns {PackageInfo} + */ +async function findPackageInfo(file: string): Promise { + const info: PackageInfo = { + name: '$unknown', + version: '0.0.0-unknown', + path: '$unknown' + }; + let path = await findClosestPkgJsonPath(file, (pkgPath) => { + const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')); + if (pkg.name != null) { + info.name = pkg.name; + if (pkg.version != null) { + info.version = pkg.version; + } + info.svelte = pkg.svelte; + return true; + } + return false; + }); + // return normalized path with appended '/' so .startsWith works for future file checks + path = normalizePath(dirname(path ?? file)) + '/'; + info.path = path; + return info; } diff --git a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-stats.ts b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-stats.ts index 7d6e3b4d8..5cea5b3c5 100644 --- a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-stats.ts +++ b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-stats.ts @@ -1,10 +1,7 @@ import { log } from './log'; -//eslint-disable-next-line node/no-missing-import -import { findClosestPkgJsonPath } from 'vitefu'; -import { readFileSync } from 'fs'; -import { dirname } from 'path'; import { performance } from 'perf_hooks'; import { normalizePath } from 'vite'; +import { VitePluginSvelteCache } from './vite-plugin-svelte-cache'; interface Stat { file: string; @@ -87,31 +84,13 @@ function formatPackageStats(pkgStats: PackageStats[]) { return table; } -/** - * utility to get the package name a file belongs to - * - * @param {string} file to find package for - * @returns {path:string,name:string} tuple of path,name where name is the parsed package name and path is the normalized path to it - */ -async function getClosestNamedPackage(file: string): Promise<{ name: string; path: string }> { - let name = '$unknown'; - let path = await findClosestPkgJsonPath(file, (pkgPath) => { - const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8')); - if (pkg.name != null) { - name = pkg.name; - return true; - } - return false; - }); - // return normalized path with appended '/' so .startsWith works for future file checks - path = normalizePath(dirname(path ?? file)) + '/'; - return { name, path }; -} - export class VitePluginSvelteStats { // package directory -> package name - private _packages: { path: string; name: string }[] = []; + private _cache: VitePluginSvelteCache; private _collections: StatCollection[] = []; + constructor(cache: VitePluginSvelteCache) { + this._cache = cache; + } startCollection(name: string, opts?: Partial) { const options = { ...defaultCollectionOptions, @@ -186,12 +165,7 @@ export class VitePluginSvelteStats { private async _aggregateStatsResult(collection: StatCollection) { const stats = collection.stats; for (const stat of stats) { - let pkg = this._packages.find((p) => stat.file.startsWith(p.path)); - if (!pkg) { - pkg = await getClosestNamedPackage(stat.file); - this._packages.push(pkg); - } - stat.pkg = pkg.name; + stat.pkg = (await this._cache.getPackageInfo(stat.file)).name; } // group stats From 81bfa52ec04e881c1486080b7eed1d4b46942379 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 24 Nov 2022 13:31:03 +0100 Subject: [PATCH 02/14] perf: only compare svelte resolve with vite resolve on first occurrence --- packages/vite-plugin-svelte/src/index.ts | 10 +++++----- .../src/utils/vite-plugin-svelte-cache.ts | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 8760c0b15..0c5a5f2d4 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -167,17 +167,18 @@ export function svelte(inlineOptions?: Partial): Plugin[] { // for ssr, during scanning and non-prebundled, we do it if (ssr || scan || !isPrebundled) { try { + const isFirstResolve = !cache.hasResolvedSvelteField(importee, importer); const resolved = await resolveViaPackageJsonSvelte(importee, importer, cache); - if (resolved) { + if (isFirstResolve && resolved) { + const packageInfo = await cache.getPackageInfo(resolved); log.debug.once( - `resolveId resolved ${resolved} via package.json svelte field of ${importee}` + `resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageInfo.name}@${packageInfo.version}` ); try { const viteResolved = ( await this.resolve(importee, importer, { ...opts, skipSelf: true }) )?.id; if (resolved !== viteResolved) { - const packageInfo = await cache.getPackageInfo(resolved); log.warn.once( `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files differently than standard vite resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.` ); @@ -188,9 +189,8 @@ export function svelte(inlineOptions?: Partial): Plugin[] { `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files but standard vite resolve failed to resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.` ); } - - return resolved; } + return resolved; } catch (e) { log.debug.once( `error trying to resolve ${importee} from ${importer} via package.json svelte field `, diff --git a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts index 370c09853..b9005f0d5 100644 --- a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts +++ b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts @@ -123,6 +123,9 @@ export class VitePluginSvelteCache { return this._resolvedSvelteFields.get(this._getResolvedSvelteFieldKey(name, importer)); } + public hasResolvedSvelteField(name: string, importer?: string) { + return this._resolvedSvelteFields.has(this._getResolvedSvelteFieldKey(name, importer)); + } public setResolvedSvelteField( importee: string, importer: string | undefined = undefined, From f8c856be0f271075453e859a32627abd5c0033fb Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 24 Nov 2022 13:33:12 +0100 Subject: [PATCH 03/14] fix: add vite resolve error to log --- packages/vite-plugin-svelte/src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 0c5a5f2d4..9d3815b28 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -186,7 +186,8 @@ export function svelte(inlineOptions?: Partial): Plugin[] { } catch (e) { const packageInfo = await cache.getPackageInfo(resolved); log.warn.once( - `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files but standard vite resolve failed to resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.` + `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files but standard vite resolve failed to resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.`, + e ); } } From 4a6f5f03510b9d5d29fc5a08519ab6c71b832c65 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 24 Nov 2022 14:07:31 +0100 Subject: [PATCH 04/14] docs: add faq entry for svelte field deprecation --- docs/faq.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/faq.md b/docs/faq.md index 2fc34ecd7..7cd0c84e6 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -177,3 +177,30 @@ There is no golden rule, but you can follow these recommendations: This warning only occurs if you use non-default settings in your vite config that can cause problems in combination with prebundleSvelteLibraries. You should not use prebundleSvelteLibraries during build or for ssr, disable one of the incompatible options to make that warning (and subsequent errors) go away. + +### deprecated "svelte" field in package.json + +In the past, Svelte recommended using the custom "svelte" field in package.json to allow libraries to point at .svelte source files. +This field requires a custom implementation to resolve, so you have to use a bundler plugin and this plugin needs to implement resolving. +Since then, node has added support for [conditional exports](https://nodejs.org/api/packages.html#conditional-exports), which have more generic support in bundlers and node itself. So to increase the compatibility with the wider ecosystem and reduce the implementation needs for current and future bundler plugins, it is recommended that packages use the "svelte" exports condition. + +Example: + +```diff +// package.json +- "svelte": "src/index.js" ++ "exports": { ++ "./package.json": "./package.json", ++ "./*": { ++ "svelte": "./src/*", ++ }, ++ ".": { ++ "svelte": "./index.js" ++ } + } +``` + +> **Support for the svelte field is deprecated and is going to be removed in a future version of vite-plugin-svelte.** +> +> Library authors are highly encouraged to update their packages to the new exports condition as outlined above. Check out +> [svelte-package](https://kit.svelte.dev/docs/packaging) which already supports it. From 833514472c5228db448787a2b77ffa24228da349 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 24 Nov 2022 14:24:33 +0100 Subject: [PATCH 05/14] fix: add FAQ entry and link to it from logs, refactor logging code a bit --- docs/faq.md | 4 +++- packages/vite-plugin-svelte/src/index.ts | 19 +++++++++++-------- .../vite-plugin-svelte/src/utils/constants.ts | 3 +++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 7cd0c84e6..70efede53 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -178,7 +178,9 @@ There is no golden rule, but you can follow these recommendations: This warning only occurs if you use non-default settings in your vite config that can cause problems in combination with prebundleSvelteLibraries. You should not use prebundleSvelteLibraries during build or for ssr, disable one of the incompatible options to make that warning (and subsequent errors) go away. -### deprecated "svelte" field in package.json + + +### deprecated "svelte" field In the past, Svelte recommended using the custom "svelte" field in package.json to allow libraries to point at .svelte source files. This field requires a custom implementation to resolve, so you have to use a bundler plugin and this plugin needs to implement resolving. diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 9d3815b28..77fb92c87 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -23,6 +23,7 @@ import { toRollupError } from './utils/error'; import { saveSvelteMetadata } from './utils/optimizer'; import { svelteInspector } from './ui/inspector/plugin'; import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache'; +import { FAQ_LINK_DEPRECATED_SVELTE_FIELD } from './utils/constants'; interface PluginAPI { /** @@ -174,21 +175,23 @@ export function svelte(inlineOptions?: Partial): Plugin[] { log.debug.once( `resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageInfo.name}@${packageInfo.version}` ); + const logDeprecationWarning = (error?: Error) => { + const prefix = `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\``; + const reason = error + ? ' and without it resolve would fail with the following error:' + : ' and vite would resolve differently without it.'; + const secondLine = `Packages should use the "svelte" exports condition instead. See ${FAQ_LINK_DEPRECATED_SVELTE_FIELD} for more information.`; + log.warn.once(`${prefix}${reason}\n${secondLine}`, error); + }; try { const viteResolved = ( await this.resolve(importee, importer, { ...opts, skipSelf: true }) )?.id; if (resolved !== viteResolved) { - log.warn.once( - `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files differently than standard vite resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.` - ); + logDeprecationWarning(); } } catch (e) { - const packageInfo = await cache.getPackageInfo(resolved); - log.warn.once( - `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\` which resolves .svelte files but standard vite resolve failed to resolve.\nUsing the "svelte" field in package.json is deprecated and packages should use the "svelte" exports condition instead. See :LINK HERE: for more information.`, - e - ); + logDeprecationWarning(e); } } return resolved; diff --git a/packages/vite-plugin-svelte/src/utils/constants.ts b/packages/vite-plugin-svelte/src/utils/constants.ts index a300b7d75..8548227d9 100644 --- a/packages/vite-plugin-svelte/src/utils/constants.ts +++ b/packages/vite-plugin-svelte/src/utils/constants.ts @@ -20,3 +20,6 @@ export const SVELTE_HMR_IMPORTS = [ ]; export const SVELTE_EXPORT_CONDITIONS = ['svelte']; + +export const FAQ_LINK_DEPRECATED_SVELTE_FIELD = + 'https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#deprecated-svelte-field'; From 4468262950cd8ecf1ebfa9094e781b5327cf86f5 Mon Sep 17 00:00:00 2001 From: dominikg Date: Thu, 24 Nov 2022 14:30:34 +0100 Subject: [PATCH 06/14] fix: log resolve difference --- packages/vite-plugin-svelte/src/index.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 77fb92c87..9d470d918 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -175,7 +175,7 @@ export function svelte(inlineOptions?: Partial): Plugin[] { log.debug.once( `resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageInfo.name}@${packageInfo.version}` ); - const logDeprecationWarning = (error?: Error) => { + const logDeprecationWarning = (error?: Error | string) => { const prefix = `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\``; const reason = error ? ' and without it resolve would fail with the following error:' @@ -188,7 +188,9 @@ export function svelte(inlineOptions?: Partial): Plugin[] { await this.resolve(importee, importer, { ...opts, skipSelf: true }) )?.id; if (resolved !== viteResolved) { - logDeprecationWarning(); + logDeprecationWarning( + `'${importee}' resolved to '${resolved}' but vite would resolve to '${viteResolved}'` + ); } } catch (e) { logDeprecationWarning(e); From 3f86ab0fce38d2f780c39039feef05b106c73d95 Mon Sep 17 00:00:00 2001 From: dominikg Date: Sat, 1 Apr 2023 22:53:14 +0200 Subject: [PATCH 07/14] refactor: log reduced warning at buildEnd, log details during debug only. add flag to disable warning log. --- docs/config.md | 7 ++++ .../svelte-exports-simple/index.svelte.js | 1 + .../svelte-exports-simple/package.json | 2 + .../resolve-exports-svelte/vite.config.js | 7 +++- packages/vite-plugin-svelte/src/index.ts | 39 ++++++++++++------- .../vite-plugin-svelte/src/utils/options.ts | 7 ++++ 6 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 packages/e2e-tests/_test_dependencies/svelte-exports-simple/index.svelte.js diff --git a/docs/config.md b/docs/config.md index cbe9ae0ff..bf554ce5c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -410,3 +410,10 @@ export default { - **Default:** `false` disable svelte compile statistics. + +### disableSvelteResolveWarnings + +- **Type** `boolean` +- **Default:** `false` + + disable svelte resolve warnings diff --git a/packages/e2e-tests/_test_dependencies/svelte-exports-simple/index.svelte.js b/packages/e2e-tests/_test_dependencies/svelte-exports-simple/index.svelte.js new file mode 100644 index 000000000..b4b6c06c2 --- /dev/null +++ b/packages/e2e-tests/_test_dependencies/svelte-exports-simple/index.svelte.js @@ -0,0 +1 @@ +export { default as Dependency } from './src/components/Dependency.svelte'; diff --git a/packages/e2e-tests/_test_dependencies/svelte-exports-simple/package.json b/packages/e2e-tests/_test_dependencies/svelte-exports-simple/package.json index 693614b90..5e2a0c5c3 100644 --- a/packages/e2e-tests/_test_dependencies/svelte-exports-simple/package.json +++ b/packages/e2e-tests/_test_dependencies/svelte-exports-simple/package.json @@ -6,6 +6,8 @@ "e2e-test-dep-cjs-only": "file:../cjs-only" }, "type": "module", + "files": ["src","index.svelte.js","index.js"], + "svelte": "./index.svelte.js", "exports": { "./*": { "svelte": "./src/components/*" diff --git a/packages/e2e-tests/resolve-exports-svelte/vite.config.js b/packages/e2e-tests/resolve-exports-svelte/vite.config.js index 375290c9e..73bf7171f 100644 --- a/packages/e2e-tests/resolve-exports-svelte/vite.config.js +++ b/packages/e2e-tests/resolve-exports-svelte/vite.config.js @@ -3,5 +3,10 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [svelte({ compilerOptions: { css: 'none' } })] + plugins: [ + svelte({ + compilerOptions: { css: 'none' }, + experimental: { disableSvelteResolveWarnings: false } + }) + ] }); diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 1fc1440ef..56aaaf4e4 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -51,6 +51,7 @@ export function svelte(inlineOptions?: Partial): Plugin[] { /* eslint-enable no-unused-vars */ let resolvedSvelteSSR: Promise; + let packagesWithResolveWarnings: Set; const api: PluginAPI = {}; const plugins: Plugin[] = [ { @@ -85,6 +86,7 @@ export function svelte(inlineOptions?: Partial): Plugin[] { }, async buildStart() { + packagesWithResolveWarnings = new Set(); if (!options.prebundleSvelteLibraries) return; const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options); if (isSvelteMetadataChanged) { @@ -169,28 +171,29 @@ export function svelte(inlineOptions?: Partial): Plugin[] { const resolved = await resolveViaPackageJsonSvelte(importee, importer, cache); if (isFirstResolve && resolved) { const packageInfo = await cache.getPackageInfo(resolved); + const packageVersion = `${packageInfo.name}@${packageInfo.version}`; log.debug.once( - `resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageInfo.name}@${packageInfo.version}` + `resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageVersion}` ); - const logDeprecationWarning = (error?: Error | string) => { - const prefix = `DEPRECATION WARNING: ${packageInfo.name}@${packageInfo.version} package.json has \`"svelte":"${packageInfo.svelte}"\``; - const reason = error - ? ' and without it resolve would fail with the following error:' - : ' and vite would resolve differently without it.'; - const secondLine = `Packages should use the "svelte" exports condition instead. See ${FAQ_LINK_DEPRECATED_SVELTE_FIELD} for more information.`; - log.warn.once(`${prefix}${reason}\n${secondLine}`, error); - }; + try { const viteResolved = ( await this.resolve(importee, importer, { ...opts, skipSelf: true }) )?.id; if (resolved !== viteResolved) { - logDeprecationWarning( - `'${importee}' resolved to '${resolved}' but vite would resolve to '${viteResolved}'` - ); + packagesWithResolveWarnings.add(packageVersion); + log.debug.enabled && + log.debug.once( + `resolve difference for ${packageVersion} ${importee} - svelte: "${resolved}", vite: "${viteResolved}"` + ); } } catch (e) { - logDeprecationWarning(e); + packagesWithResolveWarnings.add(packageVersion); + log.debug.enabled && + log.debug.once( + `resolve error for ${packageVersion} ${importee} - svelte: "${resolved}", vite: ERROR`, + e + ); } } return resolved; @@ -247,6 +250,16 @@ export function svelte(inlineOptions?: Partial): Plugin[] { }, async buildEnd() { await options.stats?.finishAll(); + if ( + !options.experimental?.disableSvelteResolveWarnings && + packagesWithResolveWarnings?.size > 0 + ) { + log.warn( + `WARNING: The following packages use a svelte resolve configuration in package.json that is going to cause problems in the future.\n\n${[ + ...packagesWithResolveWarnings + ].join('\n')}\n\nPlease see ${FAQ_LINK_DEPRECATED_SVELTE_FIELD} for details.` + ); + } } } ]; diff --git a/packages/vite-plugin-svelte/src/utils/options.ts b/packages/vite-plugin-svelte/src/utils/options.ts index d0c75c5eb..4aa50674b 100644 --- a/packages/vite-plugin-svelte/src/utils/options.ts +++ b/packages/vite-plugin-svelte/src/utils/options.ts @@ -734,6 +734,13 @@ export interface ExperimentalOptions { * @default false */ disableCompileStats?: 'dev' | 'build' | boolean; + + /** + * disable svelte field resolve warnings + * + * @default false + */ + disableSvelteResolveWarnings?: boolean; } export interface InspectorOptions { From 63fe891dc680c2d34f64bd9bfef7546ae89786ef Mon Sep 17 00:00:00 2001 From: dominikg Date: Sat, 1 Apr 2023 23:00:51 +0200 Subject: [PATCH 08/14] docs: update wording and improve exports map example --- docs/faq.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 0a24453ee..c5b8e6e01 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -221,11 +221,10 @@ Example: ```diff // package.json -- "svelte": "src/index.js" + "svelte": "src/index.js", + "exports": { -+ "./package.json": "./package.json", + "./*": { -+ "svelte": "./src/*", ++ "svelte": "./src/lib/components/*", + }, + ".": { + "svelte": "./index.js" @@ -233,7 +232,9 @@ Example: } ``` -> **Support for the svelte field is deprecated and is going to be removed in a future version of vite-plugin-svelte.** +> **Support for the svelte field is going to be deprecated and later removed in a future versions of vite-plugin-svelte.** > -> Library authors are highly encouraged to update their packages to the new exports condition as outlined above. Check out +> Library authors are highly encouraged to update their packages to add the new exports condition as outlined above. Check out > [svelte-package](https://kit.svelte.dev/docs/packaging) which already supports it. +> +> For backwards compatibility, you can keep the svelte field in addition to the exports condition. But make sure that both always resolve to the same files. From e24d839afdf89a20b39a80cb7b98f86bfff27c1c Mon Sep 17 00:00:00 2001 From: Dominik G Date: Sat, 8 Apr 2023 21:23:46 +0200 Subject: [PATCH 09/14] Apply suggestions from code review Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- docs/config.md | 2 +- packages/e2e-tests/resolve-exports-svelte/vite.config.js | 3 +-- .../vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/config.md b/docs/config.md index bf554ce5c..5342edbf6 100644 --- a/docs/config.md +++ b/docs/config.md @@ -416,4 +416,4 @@ export default { - **Type** `boolean` - **Default:** `false` - disable svelte resolve warnings + disable svelte resolve warnings. Note: this is highly discouraged and you should instead fix these packages which will break in the future. diff --git a/packages/e2e-tests/resolve-exports-svelte/vite.config.js b/packages/e2e-tests/resolve-exports-svelte/vite.config.js index 73bf7171f..deab083b9 100644 --- a/packages/e2e-tests/resolve-exports-svelte/vite.config.js +++ b/packages/e2e-tests/resolve-exports-svelte/vite.config.js @@ -5,8 +5,7 @@ import { svelte } from '@sveltejs/vite-plugin-svelte'; export default defineConfig({ plugins: [ svelte({ - compilerOptions: { css: 'none' }, - experimental: { disableSvelteResolveWarnings: false } + compilerOptions: { css: 'none' } }) ] }); diff --git a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts index b9005f0d5..c5ade5655 100644 --- a/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts +++ b/packages/vite-plugin-svelte/src/utils/vite-plugin-svelte-cache.ts @@ -152,7 +152,7 @@ export class VitePluginSvelteCache { } /** - * utility to get some info from the package.json a file belongs to (closest one with a "name" set) + * utility to get some info from the closest package.json with a "name" set * * @param {string} file to find info for * @returns {PackageInfo} From 8a4684791eec6bb4e20d6ca36d20fc7e3ff6d210 Mon Sep 17 00:00:00 2001 From: dominikg Date: Sun, 16 Apr 2023 19:50:19 +0200 Subject: [PATCH 10/14] docs: use more neutral language for resolve differences, svelte field isn't deprecated yet --- docs/faq.md | 4 +++- packages/vite-plugin-svelte/src/index.ts | 6 +++--- packages/vite-plugin-svelte/src/utils/constants.ts | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index e46a753b7..da2f8580a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -211,7 +211,9 @@ module.exports = { -### deprecated "svelte" field +### conflicts in svelte resolve + +| If you see a warning logged for this when using a svelte library, please tell the library maintainers. In the past, Svelte recommended using the custom "svelte" field in package.json to allow libraries to point at .svelte source files. This field requires a custom implementation to resolve, so you have to use a bundler plugin and this plugin needs to implement resolving. diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 56aaaf4e4..68fbc3304 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -24,7 +24,7 @@ import { saveSvelteMetadata } from './utils/optimizer'; import { svelteInspector } from './ui/inspector/plugin'; import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache'; import { loadRaw } from './utils/load-raw'; -import { FAQ_LINK_DEPRECATED_SVELTE_FIELD } from './utils/constants'; +import { FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE } from './utils/constants'; interface PluginAPI { /** @@ -255,9 +255,9 @@ export function svelte(inlineOptions?: Partial): Plugin[] { packagesWithResolveWarnings?.size > 0 ) { log.warn( - `WARNING: The following packages use a svelte resolve configuration in package.json that is going to cause problems in the future.\n\n${[ + `WARNING: The following packages use a svelte resolve configuration in package.json that has conflicting results and is going to cause problems future.\n\n${[ ...packagesWithResolveWarnings - ].join('\n')}\n\nPlease see ${FAQ_LINK_DEPRECATED_SVELTE_FIELD} for details.` + ].join('\n')}\n\nPlease see ${FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE} for details.` ); } } diff --git a/packages/vite-plugin-svelte/src/utils/constants.ts b/packages/vite-plugin-svelte/src/utils/constants.ts index 00d9a315e..09adace6e 100644 --- a/packages/vite-plugin-svelte/src/utils/constants.ts +++ b/packages/vite-plugin-svelte/src/utils/constants.ts @@ -21,5 +21,5 @@ export const SVELTE_HMR_IMPORTS = [ export const SVELTE_EXPORT_CONDITIONS = ['svelte']; -export const FAQ_LINK_DEPRECATED_SVELTE_FIELD = - 'https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#deprecated-svelte-field'; +export const FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE = + 'https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#conflicts-in-svelte-resolve'; From c66e2f5f277b635a6ca2a53829c34993f9d75a7e Mon Sep 17 00:00:00 2001 From: Dominik G Date: Tue, 18 Apr 2023 12:05:58 +0200 Subject: [PATCH 11/14] Apply suggestions from code review Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- .changeset/cold-horses-begin.md | 2 +- docs/faq.md | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.changeset/cold-horses-begin.md b/.changeset/cold-horses-begin.md index de5bc2c54..b777411d1 100644 --- a/.changeset/cold-horses-begin.md +++ b/.changeset/cold-horses-begin.md @@ -2,4 +2,4 @@ '@sveltejs/vite-plugin-svelte': patch --- -log deprecation warnings for packages that use the "svelte" field to resolve svelte files differently than standard vite resolve +log warnings for packages that use the `svelte` field to resolve Svelte files differently than standard Vite resolve diff --git a/docs/faq.md b/docs/faq.md index da2f8580a..5ebc6afbf 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -215,9 +215,9 @@ module.exports = { | If you see a warning logged for this when using a svelte library, please tell the library maintainers. -In the past, Svelte recommended using the custom "svelte" field in package.json to allow libraries to point at .svelte source files. +In the past, Svelte recommended using the custom `svelte` field in `package.json` to allow libraries to point at `.svelte` source files. This field requires a custom implementation to resolve, so you have to use a bundler plugin and this plugin needs to implement resolving. -Since then, node has added support for [conditional exports](https://nodejs.org/api/packages.html#conditional-exports), which have more generic support in bundlers and node itself. So to increase the compatibility with the wider ecosystem and reduce the implementation needs for current and future bundler plugins, it is recommended that packages use the "svelte" exports condition. +Since then, Node has added support for [conditional exports](https://nodejs.org/api/packages.html#conditional-exports), which have more generic support in bundlers and Node itself. So to increase the compatibility with the wider ecosystem and reduce the implementation needs for current and future bundler plugins, it is recommended that packages use the `svelte` exports condition. Example: @@ -234,9 +234,7 @@ Example: } ``` -> **Support for the svelte field is going to be deprecated and later removed in a future versions of vite-plugin-svelte.** -> > Library authors are highly encouraged to update their packages to add the new exports condition as outlined above. Check out > [svelte-package](https://kit.svelte.dev/docs/packaging) which already supports it. > -> For backwards compatibility, you can keep the svelte field in addition to the exports condition. But make sure that both always resolve to the same files. +> For backwards compatibility, you can keep the `svelte` field in addition to the `exports` condition. But make sure that both always resolve to the same files. From d1e7e2d4f0aaeb06a0f60310a781f4a41a6bfd0c Mon Sep 17 00:00:00 2001 From: dominikg Date: Wed, 19 Apr 2023 00:50:30 +0200 Subject: [PATCH 12/14] docs: remove glob exports from example, add note about deep exports --- docs/faq.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 5ebc6afbf..9ecde0213 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -223,17 +223,18 @@ Example: ```diff // package.json - "svelte": "src/index.js", + "files": ["dist"], + "svelte": "dist/index.js", + "exports": { -+ "./*": { -+ "svelte": "./src/lib/components/*", -+ }, + ".": { -+ "svelte": "./index.js" ++ "svelte": "./dist/index.js" + } } ``` +You can also add individual exports of .svelte files in the exports map which gives users a choice to also use deep imports. +See the faq about [vite and prebundling](#what-is-going-on-with-vite-and-pre-bundling-dependencies) why they can be useful at times. + > Library authors are highly encouraged to update their packages to add the new exports condition as outlined above. Check out > [svelte-package](https://kit.svelte.dev/docs/packaging) which already supports it. > From 13abab43958d1b2cb9298930d18fada1c3e17020 Mon Sep 17 00:00:00 2001 From: Dominik G Date: Wed, 19 Apr 2023 06:59:15 +0200 Subject: [PATCH 13/14] docs: spelling Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 9ecde0213..a620c51fb 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -213,7 +213,7 @@ module.exports = { ### conflicts in svelte resolve -| If you see a warning logged for this when using a svelte library, please tell the library maintainers. +| If you see a warning logged for this when using a Svelte library, please tell the library maintainers. In the past, Svelte recommended using the custom `svelte` field in `package.json` to allow libraries to point at `.svelte` source files. This field requires a custom implementation to resolve, so you have to use a bundler plugin and this plugin needs to implement resolving. From e1b1e6d18d492a9f5676f2fcdf6adcc15bb104e8 Mon Sep 17 00:00:00 2001 From: dominikg Date: Wed, 19 Apr 2023 20:04:26 +0200 Subject: [PATCH 14/14] chore: fix changeset to use minor --- .changeset/cold-horses-begin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/cold-horses-begin.md b/.changeset/cold-horses-begin.md index b777411d1..394a9a24e 100644 --- a/.changeset/cold-horses-begin.md +++ b/.changeset/cold-horses-begin.md @@ -1,5 +1,5 @@ --- -'@sveltejs/vite-plugin-svelte': patch +'@sveltejs/vite-plugin-svelte': minor --- log warnings for packages that use the `svelte` field to resolve Svelte files differently than standard Vite resolve