Skip to content

Commit 2cd6475

Browse files
dominikgbenmccann
andauthored
feat: log warning if svelte field causes difference in resolve (#510)
* feat: log deprecation warning if svelte field causes difference in resolve * perf: only compare svelte resolve with vite resolve on first occurrence * fix: add vite resolve error to log * docs: add faq entry for svelte field deprecation * fix: add FAQ entry and link to it from logs, refactor logging code a bit * fix: log resolve difference * refactor: log reduced warning at buildEnd, log details during debug only. add flag to disable warning log. * docs: update wording and improve exports map example * Apply suggestions from code review Co-authored-by: Ben McCann <[email protected]> * docs: use more neutral language for resolve differences, svelte field isn't deprecated yet * Apply suggestions from code review Co-authored-by: Ben McCann <[email protected]> * docs: remove glob exports from example, add note about deep exports * docs: spelling Co-authored-by: Ben McCann <[email protected]> * chore: fix changeset to use minor --------- Co-authored-by: Ben McCann <[email protected]>
1 parent 29ecb45 commit 2cd6475

File tree

12 files changed

+169
-42
lines changed

12 files changed

+169
-42
lines changed

.changeset/cold-horses-begin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/vite-plugin-svelte': minor
3+
---
4+
5+
log warnings for packages that use the `svelte` field to resolve Svelte files differently than standard Vite resolve

docs/config.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,10 @@ export default {
403403
rawWarnings: Warning[]; // raw compiler output
404404
};
405405
```
406+
407+
### disableSvelteResolveWarnings
408+
409+
- **Type** `boolean`
410+
- **Default:** `false`
411+
412+
disable svelte resolve warnings. Note: this is highly discouraged and you should instead fix these packages which will break in the future.

docs/faq.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,34 @@ module.exports = {
208208
+ }
209209
}
210210
```
211+
212+
<!-- the following header generates an anchor that is used in logging, do not modify!-->
213+
214+
### conflicts in svelte resolve
215+
216+
| If you see a warning logged for this when using a Svelte library, please tell the library maintainers.
217+
218+
In the past, Svelte recommended using the custom `svelte` field in `package.json` to allow libraries to point at `.svelte` source files.
219+
This field requires a custom implementation to resolve, so you have to use a bundler plugin and this plugin needs to implement resolving.
220+
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.
221+
222+
Example:
223+
224+
```diff
225+
// package.json
226+
"files": ["dist"],
227+
"svelte": "dist/index.js",
228+
+ "exports": {
229+
+ ".": {
230+
+ "svelte": "./dist/index.js"
231+
+ }
232+
}
233+
```
234+
235+
You can also add individual exports of .svelte files in the exports map which gives users a choice to also use deep imports.
236+
See the faq about [vite and prebundling](#what-is-going-on-with-vite-and-pre-bundling-dependencies) why they can be useful at times.
237+
238+
> Library authors are highly encouraged to update their packages to add the new exports condition as outlined above. Check out
239+
> [svelte-package](https://kit.svelte.dev/docs/packaging) which already supports it.
240+
>
241+
> 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.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as Dependency } from './src/components/Dependency.svelte';

packages/e2e-tests/_test_dependencies/svelte-exports-simple/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
"type": "module",
99
"files": [
1010
"src",
11-
"index.js"
11+
"index.js",
12+
"index.svelte.js"
1213
],
14+
"svelte": "./index.svelte.js",
1315
"exports": {
1416
"./*": {
1517
"svelte": "./src/components/*"

packages/e2e-tests/resolve-exports-svelte/vite.config.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@ import { svelte } from '@sveltejs/vite-plugin-svelte';
33

44
// https://vitejs.dev/config/
55
export default defineConfig({
6-
plugins: [svelte({ compilerOptions: { css: 'none' } })]
6+
plugins: [
7+
svelte({
8+
compilerOptions: { css: 'none' }
9+
})
10+
]
711
});

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

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { saveSvelteMetadata } from './utils/optimizer';
3232
import { svelteInspector } from './ui/inspector/plugin';
3333
import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache';
3434
import { loadRaw } from './utils/load-raw';
35+
import { FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE } from './utils/constants';
3536

3637
interface PluginAPI {
3738
/**
@@ -61,6 +62,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
6162
/* eslint-enable no-unused-vars */
6263

6364
let resolvedSvelteSSR: Promise<PartialResolvedId | null>;
65+
let packagesWithResolveWarnings: Set<string>;
6466
const api: PluginAPI = {};
6567
const plugins: Plugin[] = [
6668
{
@@ -84,7 +86,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
8486
},
8587

8688
async configResolved(config) {
87-
options = resolveOptions(options, config);
89+
options = resolveOptions(options, config, cache);
8890
patchResolvedViteConfig(config, options);
8991
requestParser = buildIdParser(options);
9092
compileSvelte = createCompileSvelte(options);
@@ -95,6 +97,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
9597
},
9698

9799
async buildStart() {
100+
packagesWithResolveWarnings = new Set<string>();
98101
if (!options.prebundleSvelteLibraries) return;
99102
const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options);
100103
if (isSvelteMetadataChanged) {
@@ -176,13 +179,36 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
176179
// for ssr, during scanning and non-prebundled, we do it
177180
if (ssr || scan || !isPrebundled) {
178181
try {
182+
const isFirstResolve = !cache.hasResolvedSvelteField(importee, importer);
179183
const resolved = await resolveViaPackageJsonSvelte(importee, importer, cache);
180-
if (resolved) {
181-
log.debug(
182-
`resolveId resolved ${resolved} via package.json svelte field of ${importee}`
184+
if (isFirstResolve && resolved) {
185+
const packageInfo = await cache.getPackageInfo(resolved);
186+
const packageVersion = `${packageInfo.name}@${packageInfo.version}`;
187+
log.debug.once(
188+
`resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageVersion}`
183189
);
184-
return resolved;
190+
191+
try {
192+
const viteResolved = (
193+
await this.resolve(importee, importer, { ...opts, skipSelf: true })
194+
)?.id;
195+
if (resolved !== viteResolved) {
196+
packagesWithResolveWarnings.add(packageVersion);
197+
log.debug.enabled &&
198+
log.debug.once(
199+
`resolve difference for ${packageVersion} ${importee} - svelte: "${resolved}", vite: "${viteResolved}"`
200+
);
201+
}
202+
} catch (e) {
203+
packagesWithResolveWarnings.add(packageVersion);
204+
log.debug.enabled &&
205+
log.debug.once(
206+
`resolve error for ${packageVersion} ${importee} - svelte: "${resolved}", vite: ERROR`,
207+
e
208+
);
209+
}
185210
}
211+
return resolved;
186212
} catch (e) {
187213
log.debug.once(
188214
`error trying to resolve ${importee} from ${importer} via package.json svelte field `,
@@ -236,6 +262,16 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
236262
},
237263
async buildEnd() {
238264
await options.stats?.finishAll();
265+
if (
266+
!options.experimental?.disableSvelteResolveWarnings &&
267+
packagesWithResolveWarnings?.size > 0
268+
) {
269+
log.warn(
270+
`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${[
271+
...packagesWithResolveWarnings
272+
].join('\n')}\n\nPlease see ${FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE} for details.`
273+
);
274+
}
239275
}
240276
}
241277
];

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@ export const SVELTE_HMR_IMPORTS = [
2020
];
2121

2222
export const SVELTE_EXPORT_CONDITIONS = ['svelte'];
23+
24+
export const FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE =
25+
'https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#conflicts-in-svelte-resolve';

packages/vite-plugin-svelte/src/utils/log.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ function createLogger(level: string): LogFn {
7979
const logFn: LogFn = _log.bind(null, logger) as LogFn;
8080
const logged = new Set<String>();
8181
const once = function (message: string, payload?: any, namespace?: string) {
82-
if (logged.has(message)) {
82+
if (!logger.enabled || logged.has(message)) {
8383
return;
8484
}
8585
logged.add(message);

packages/vite-plugin-svelte/src/utils/options.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434

3535
import { isCommonDepWithoutSvelteField } from './dependencies';
3636
import { VitePluginSvelteStats } from './vite-plugin-svelte-stats';
37+
import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
3738

3839
const allowedPluginOptions = new Set([
3940
'include',
@@ -178,7 +179,8 @@ function mergeConfigs<T>(...configs: (Partial<T> | undefined)[]): T {
178179
// also validates the final config.
179180
export function resolveOptions(
180181
preResolveOptions: PreResolvedOptions,
181-
viteConfig: ResolvedConfig
182+
viteConfig: ResolvedConfig,
183+
cache: VitePluginSvelteCache
182184
): ResolvedOptions {
183185
const css = preResolveOptions.emitCss ? 'external' : 'injected';
184186
const defaultOptions: Partial<Options> = {
@@ -207,7 +209,7 @@ export function resolveOptions(
207209
enforceOptionsForProduction(merged);
208210
// mergeConfigs would mangle functions on the stats class, so do this afterwards
209211
if (log.debug.enabled && isDebugNamespaceEnabled('stats')) {
210-
merged.stats = new VitePluginSvelteStats();
212+
merged.stats = new VitePluginSvelteStats(cache);
211213
}
212214
return merged;
213215
}
@@ -721,6 +723,13 @@ export interface ExperimentalOptions {
721723
*
722724
*/
723725
sendWarningsToBrowser?: boolean;
726+
727+
/**
728+
* disable svelte field resolve warnings
729+
*
730+
* @default false
731+
*/
732+
disableSvelteResolveWarnings?: boolean;
724733
}
725734

726735
export interface InspectorOptions {

0 commit comments

Comments
 (0)