@sveltejs/vite-plugin-svelte 2.0.4 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sveltejs/vite-plugin-svelte",
3
- "version": "2.0.4",
3
+ "version": "2.1.0",
4
4
  "license": "MIT",
5
5
  "author": "dominikg",
6
6
  "files": [
@@ -50,11 +50,11 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/debug": "^4.1.7",
53
- "esbuild": "^0.17.12",
53
+ "esbuild": "^0.17.17",
54
54
  "rollup": "^2.79.1",
55
- "svelte": "^3.57.0",
55
+ "svelte": "^3.58.0",
56
56
  "tsup": "^6.7.0",
57
- "vite": "^4.2.1"
57
+ "vite": "^4.3.1"
58
58
  },
59
59
  "scripts": {
60
60
  "dev": "pnpm build:ci --sourcemap --watch src",
@@ -0,0 +1,3 @@
1
+ .foo {
2
+ color: green;
3
+ }
@@ -0,0 +1,51 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { vitePreprocess } from '../preprocess';
3
+ import path from 'path';
4
+ import { normalizePath } from 'vite';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ const fixtureDir = normalizePath(
8
+ path.join(path.dirname(fileURLToPath(import.meta.url)), 'fixtures', 'preprocess')
9
+ );
10
+
11
+ describe('vitePreprocess', () => {
12
+ it('returns function', () => {
13
+ const preprocessorGroup = vitePreprocess({ script: true, style: true });
14
+ expect(typeof preprocessorGroup).toBe('object');
15
+ expect(typeof preprocessorGroup.script).toBe('function');
16
+ expect(typeof preprocessorGroup.style).toBe('function');
17
+ });
18
+
19
+ describe('style', async () => {
20
+ it('produces sourcemap with relative filename', async () => {
21
+ const { style } = vitePreprocess({ style: { css: { devSourcemap: true } } });
22
+ const scss = `
23
+ @import './foo';
24
+ .foo {
25
+ &.bar {
26
+ color: red;
27
+ }
28
+ }`.replace(/\t/g, '');
29
+
30
+ const processed = await style({
31
+ content: scss,
32
+ attributes: {
33
+ lang: 'scss'
34
+ },
35
+ markup: '', // not read by vitePreprocess
36
+ filename: `${fixtureDir}/File.svelte`
37
+ });
38
+ expect(processed).toBeDefined();
39
+ // @ts-ignore
40
+ const { code, map, dependencies } = processed;
41
+ expect(code).toBe('.foo {\n color: green;\n}\n\n.foo.bar {\n color: red;\n}');
42
+ expect(map.file).toBe('File.svelte');
43
+ expect(map.sources.length).toBe(2);
44
+ expect(map.sources[0]).toBe('foo.scss');
45
+ expect(map.sources[1]).toBe('File.svelte');
46
+ expect(dependencies).toBeDefined();
47
+ expect(dependencies[0]).toBe(path.resolve(fixtureDir, 'foo.scss'));
48
+ expect(dependencies.length).toBe(1);
49
+ });
50
+ });
51
+ });
package/src/index.ts CHANGED
@@ -1,5 +1,13 @@
1
1
  import fs from 'fs';
2
- import { HmrContext, ModuleNode, Plugin, ResolvedConfig, UserConfig } from 'vite';
2
+ import { VERSION as svelteVersion } from 'svelte/compiler';
3
+ import {
4
+ HmrContext,
5
+ ModuleNode,
6
+ Plugin,
7
+ ResolvedConfig,
8
+ UserConfig,
9
+ version as viteVersion
10
+ } from 'vite';
3
11
  // eslint-disable-next-line node/no-missing-import
4
12
  import { isDepExcluded } from 'vitefu';
5
13
  import { handleHotUpdate } from './handle-hot-update';
@@ -24,6 +32,7 @@ import { saveSvelteMetadata } from './utils/optimizer';
24
32
  import { svelteInspector } from './ui/inspector/plugin';
25
33
  import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache';
26
34
  import { loadRaw } from './utils/load-raw';
35
+ import { FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE } from './utils/constants';
27
36
 
28
37
  interface PluginAPI {
29
38
  /**
@@ -35,6 +44,9 @@ interface PluginAPI {
35
44
  // TODO expose compile cache here so other utility plugins can use it
36
45
  }
37
46
 
47
+ const isVite4_0 = viteVersion.startsWith('4.0');
48
+ const isSvelte3 = svelteVersion.startsWith('3');
49
+
38
50
  export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
39
51
  if (process.env.DEBUG != null) {
40
52
  log.setLevel('debug');
@@ -50,6 +62,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
50
62
  /* eslint-enable no-unused-vars */
51
63
 
52
64
  let resolvedSvelteSSR: Promise<PartialResolvedId | null>;
65
+ let packagesWithResolveWarnings: Set<string>;
53
66
  const api: PluginAPI = {};
54
67
  const plugins: Plugin[] = [
55
68
  {
@@ -73,7 +86,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
73
86
  },
74
87
 
75
88
  async configResolved(config) {
76
- options = resolveOptions(options, config);
89
+ options = resolveOptions(options, config, cache);
77
90
  patchResolvedViteConfig(config, options);
78
91
  requestParser = buildIdParser(options);
79
92
  compileSvelte = createCompileSvelte(options);
@@ -84,6 +97,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
84
97
  },
85
98
 
86
99
  async buildStart() {
100
+ packagesWithResolveWarnings = new Set<string>();
87
101
  if (!options.prebundleSvelteLibraries) return;
88
102
  const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options);
89
103
  if (isSvelteMetadataChanged) {
@@ -135,7 +149,8 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
135
149
  }
136
150
  }
137
151
 
138
- if (ssr && importee === 'svelte') {
152
+ // TODO: remove this after bumping peerDep on Vite to 4.1+ or Svelte to 4.0+
153
+ if (isVite4_0 && isSvelte3 && ssr && importee === 'svelte') {
139
154
  if (!resolvedSvelteSSR) {
140
155
  resolvedSvelteSSR = this.resolve('svelte/ssr', undefined, { skipSelf: true }).then(
141
156
  (svelteSSR) => {
@@ -164,13 +179,36 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
164
179
  // for ssr, during scanning and non-prebundled, we do it
165
180
  if (ssr || scan || !isPrebundled) {
166
181
  try {
182
+ const isFirstResolve = !cache.hasResolvedSvelteField(importee, importer);
167
183
  const resolved = await resolveViaPackageJsonSvelte(importee, importer, cache);
168
- if (resolved) {
169
- log.debug(
170
- `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}`
171
189
  );
172
- 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
+ }
173
210
  }
211
+ return resolved;
174
212
  } catch (e) {
175
213
  log.debug.once(
176
214
  `error trying to resolve ${importee} from ${importer} via package.json svelte field `,
@@ -224,6 +262,16 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
224
262
  },
225
263
  async buildEnd() {
226
264
  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
+ }
227
275
  }
228
276
  }
229
277
  ];
package/src/preprocess.ts CHANGED
@@ -2,11 +2,13 @@ import { preprocessCSS, resolveConfig, transformWithEsbuild } from 'vite';
2
2
  import type { ESBuildOptions, InlineConfig, ResolvedConfig } from 'vite';
3
3
  // eslint-disable-next-line node/no-missing-import
4
4
  import type { Preprocessor, PreprocessorGroup } from 'svelte/types/compiler/preprocess';
5
- import { mapSourcesToRelative } from './utils/sourcemaps';
5
+ import { mapToRelative, removeLangSuffix } from './utils/sourcemaps';
6
6
 
7
7
  const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss', 'sss'];
8
8
  const supportedScriptLangs = ['ts'];
9
9
 
10
+ export const lang_sep = '.vite-preprocess.';
11
+
10
12
  export function vitePreprocess(opts?: {
11
13
  script?: boolean;
12
14
  style?: boolean | InlineConfig | ResolvedConfig;
@@ -39,7 +41,7 @@ function viteScript(): { script: Preprocessor } {
39
41
  }
40
42
  });
41
43
 
42
- mapSourcesToRelative(map, filename);
44
+ mapToRelative(map, filename);
43
45
 
44
46
  return {
45
47
  code,
@@ -72,14 +74,16 @@ function viteStyle(config: InlineConfig | ResolvedConfig = {}): {
72
74
  }
73
75
  transform = getCssTransformFn(resolvedConfig);
74
76
  }
75
- const moduleId = `${filename}.${lang}`;
76
- const { code, map } = await transform(content, moduleId);
77
-
78
- mapSourcesToRelative(map, moduleId);
79
-
77
+ const suffix = `${lang_sep}${lang}`;
78
+ const moduleId = `${filename}${suffix}`;
79
+ const { code, map, deps } = await transform(content, moduleId);
80
+ removeLangSuffix(map, suffix);
81
+ mapToRelative(map, filename);
82
+ const dependencies = deps ? Array.from(deps).filter((d) => !d.endsWith(suffix)) : undefined;
80
83
  return {
81
84
  code,
82
- map: map ?? undefined
85
+ map: map ?? undefined,
86
+ dependencies
83
87
  };
84
88
  };
85
89
  // @ts-expect-error tag so can be found by v-p-s
@@ -87,8 +91,12 @@ function viteStyle(config: InlineConfig | ResolvedConfig = {}): {
87
91
  return { style };
88
92
  }
89
93
 
90
- // eslint-disable-next-line no-unused-vars
91
- type CssTransform = (code: string, filename: string) => Promise<{ code: string; map?: any }>;
94
+ type CssTransform = (
95
+ // eslint-disable-next-line no-unused-vars
96
+ code: string,
97
+ // eslint-disable-next-line no-unused-vars
98
+ filename: string
99
+ ) => Promise<{ code: string; map?: any; deps?: Set<string> }>;
92
100
 
93
101
  function getCssTransformFn(config: ResolvedConfig): CssTransform {
94
102
  return async (code, filename) => {
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { removeLangSuffix, mapToRelative } from '../sourcemaps';
3
+ import { lang_sep } from '../../preprocess';
4
+ import { normalizePath } from 'vite';
5
+ import path from 'path';
6
+ import { fileURLToPath, pathToFileURL } from 'url';
7
+
8
+ const fixtureDir = normalizePath(
9
+ path.join(path.dirname(fileURLToPath(import.meta.url)), 'fixtures', 'preprocess')
10
+ );
11
+ const filename = 'File.svelte';
12
+
13
+ describe('removeLangSuffix', () => {
14
+ it('removes suffix', () => {
15
+ const suffix = `${lang_sep}scss`;
16
+ const map = {
17
+ file: `${fixtureDir}/${filename}${suffix}`,
18
+ sources: ['foo.scss', `${fixtureDir}/${filename}${suffix}`],
19
+ sourceRoot: fixtureDir
20
+ };
21
+ removeLangSuffix(map, suffix);
22
+ expect(map.file).toBe(`${fixtureDir}/${filename}`);
23
+ expect(map.sourceRoot).toBe(fixtureDir);
24
+ expect(map.sources[0]).toBe('foo.scss');
25
+ expect(map.sources[1]).toBe(`${fixtureDir}/${filename}`);
26
+ });
27
+ });
28
+
29
+ describe('mapToRelative', () => {
30
+ it('converts absolute to relative', () => {
31
+ const file = `${fixtureDir}/File.svelte`;
32
+ const map = {
33
+ file,
34
+ sources: [`${fixtureDir}/foo.scss`, file]
35
+ };
36
+ mapToRelative(map, file);
37
+ expect(map.file).toBe('File.svelte');
38
+ expect(map.sources[0]).toBe('foo.scss');
39
+ expect(map.sources[1]).toBe('File.svelte');
40
+ });
41
+
42
+ it('accounts for sourceRoot', () => {
43
+ const file = `${fixtureDir}/File.svelte`;
44
+ const sourceRoot = normalizePath(path.resolve(fixtureDir, '..'));
45
+ const rootedBase = fixtureDir.replace(sourceRoot, '');
46
+ const map = {
47
+ file,
48
+ sourceRoot,
49
+ sources: [
50
+ `${rootedBase}/foo.scss`,
51
+ `${rootedBase}/File.svelte`,
52
+ `${pathToFileURL(`${fixtureDir}/bar.scss`)}`
53
+ ]
54
+ };
55
+ mapToRelative(map, file);
56
+ expect(map.file).toBe('File.svelte');
57
+ expect(map.sources[0]).toBe('foo.scss');
58
+ expect(map.sources[1]).toBe('File.svelte');
59
+ expect(map.sources[2]).toBe('bar.scss');
60
+ expect(map.sources.length).toBe(3);
61
+ expect(map.sourceRoot).not.toBeDefined();
62
+ });
63
+
64
+ it('accounts for relative sourceRoot', () => {
65
+ const file = `${fixtureDir}/File.svelte`;
66
+ const map = {
67
+ file,
68
+ sourceRoot: './some-path/..',
69
+ sources: [`foo.scss`, `File.svelte`, `${pathToFileURL(`${fixtureDir}/bar.scss`)}`]
70
+ };
71
+ mapToRelative(map, file);
72
+ expect(map.file).toBe('File.svelte');
73
+ expect(map.sources[0]).toBe('./some-path/../foo.scss');
74
+ expect(map.sources[1]).toBe('./some-path/../File.svelte');
75
+ expect(map.sources[2]).toBe('bar.scss');
76
+ expect(map.sources.length).toBe(3);
77
+ expect(map.sourceRoot).not.toBeDefined();
78
+ });
79
+ });
@@ -9,7 +9,7 @@ import { StatCollection } from './vite-plugin-svelte-stats';
9
9
  //eslint-disable-next-line node/no-missing-import
10
10
  import type { Processed } from 'svelte/types/compiler/preprocess';
11
11
  import { createInjectScopeEverythingRulePreprocessorGroup } from './preprocess';
12
- import { mapSourcesToRelative } from './sourcemaps';
12
+ import { mapToRelative } from './sourcemaps';
13
13
 
14
14
  const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/;
15
15
 
@@ -95,7 +95,7 @@ const _createCompileSvelte = (makeHot: Function) => {
95
95
  if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
96
96
  }
97
97
  if (typeof preprocessed?.map === 'object') {
98
- mapSourcesToRelative(preprocessed?.map, filename);
98
+ mapToRelative(preprocessed?.map, filename);
99
99
  }
100
100
  if (raw && svelteRequest.query.type === 'preprocessed') {
101
101
  // shortcut
@@ -132,8 +132,8 @@ const _createCompileSvelte = (makeHot: Function) => {
132
132
  if (endStat) {
133
133
  endStat();
134
134
  }
135
- mapSourcesToRelative(compiled.js?.map, filename);
136
- mapSourcesToRelative(compiled.css?.map, filename);
135
+ mapToRelative(compiled.js?.map, filename);
136
+ mapToRelative(compiled.css?.map, filename);
137
137
  if (!raw) {
138
138
  // wire css import and code for hmr
139
139
  const hasCss = compiled.css?.code?.trim().length > 0;
@@ -20,3 +20,6 @@ export const SVELTE_HMR_IMPORTS = [
20
20
  ];
21
21
 
22
22
  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';
package/src/utils/log.ts CHANGED
@@ -3,7 +3,6 @@ import { cyan, yellow, red } from 'kleur/colors';
3
3
  import debug from 'debug';
4
4
  import { ResolvedOptions, Warning } from './options';
5
5
  import { SvelteRequest } from './id';
6
-
7
6
  const levels: string[] = ['debug', 'info', 'warn', 'error', 'silent'];
8
7
  const prefix = 'vite-plugin-svelte';
9
8
  const loggers: { [key: string]: any } = {
@@ -48,14 +47,21 @@ function setLevel(level: string) {
48
47
  }
49
48
  }
50
49
 
51
- function _log(logger: any, message: string, payload?: any) {
50
+ function _log(logger: any, message: string, payload?: any, namespace?: string) {
52
51
  if (!logger.enabled) {
53
52
  return;
54
53
  }
55
54
  if (logger.isDebug) {
56
- payload !== undefined ? logger.log(message, payload) : logger.log(message);
55
+ const log = namespace ? logger.log.extend(namespace) : logger.log;
56
+ payload !== undefined ? log(message, payload) : log(message);
57
57
  } else {
58
- logger.log(logger.color(`${new Date().toLocaleTimeString()} [${prefix}] ${message}`));
58
+ logger.log(
59
+ logger.color(
60
+ `${new Date().toLocaleTimeString()} [${prefix}${
61
+ namespace ? `:${namespace}` : ''
62
+ }] ${message}`
63
+ )
64
+ );
59
65
  if (payload) {
60
66
  logger.log(payload);
61
67
  }
@@ -63,21 +69,21 @@ function _log(logger: any, message: string, payload?: any) {
63
69
  }
64
70
 
65
71
  export interface LogFn {
66
- (message: string, payload?: any): void;
72
+ (message: string, payload?: any, namespace?: string): void;
67
73
  enabled: boolean;
68
- once: (message: string, payload?: any) => void;
74
+ once: (message: string, payload?: any, namespace?: string) => void;
69
75
  }
70
76
 
71
77
  function createLogger(level: string): LogFn {
72
78
  const logger = loggers[level];
73
79
  const logFn: LogFn = _log.bind(null, logger) as LogFn;
74
80
  const logged = new Set<String>();
75
- const once = function (message: string, payload?: any) {
76
- if (logged.has(message)) {
81
+ const once = function (message: string, payload?: any, namespace?: string) {
82
+ if (!logger.enabled || logged.has(message)) {
77
83
  return;
78
84
  }
79
85
  logged.add(message);
80
- logFn.apply(null, [message, payload]);
86
+ logFn.apply(null, [message, payload, namespace]);
81
87
  };
82
88
  Object.defineProperty(logFn, 'enabled', {
83
89
  get() {
@@ -209,3 +215,7 @@ export function buildExtendedLogMessage(w: Warning) {
209
215
  }
210
216
  return parts.join('');
211
217
  }
218
+
219
+ export function isDebugNamespaceEnabled(namespace: string) {
220
+ return debug.enabled(`vite:${prefix}:${namespace}`);
221
+ }
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-unused-vars */
2
2
  import { ConfigEnv, ResolvedConfig, UserConfig, ViteDevServer, normalizePath } from 'vite';
3
- import { log } from './log';
3
+ import { isDebugNamespaceEnabled, log } from './log';
4
4
  import { loadSvelteConfig } from './load-svelte-config';
5
5
  import {
6
6
  SVELTE_EXPORT_CONDITIONS,
@@ -34,6 +34,7 @@ import {
34
34
 
35
35
  import { isCommonDepWithoutSvelteField } from './dependencies';
36
36
  import { VitePluginSvelteStats } from './vite-plugin-svelte-stats';
37
+ import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
37
38
 
38
39
  const allowedPluginOptions = new Set([
39
40
  'include',
@@ -178,7 +179,8 @@ function mergeConfigs<T>(...configs: (Partial<T> | undefined)[]): T {
178
179
  // also validates the final config.
179
180
  export function resolveOptions(
180
181
  preResolveOptions: PreResolvedOptions,
181
- viteConfig: ResolvedConfig
182
+ viteConfig: ResolvedConfig,
183
+ cache: VitePluginSvelteCache
182
184
  ): ResolvedOptions {
183
185
  const css = preResolveOptions.emitCss ? 'external' : 'injected';
184
186
  const defaultOptions: Partial<Options> = {
@@ -206,12 +208,8 @@ export function resolveOptions(
206
208
  enforceOptionsForHmr(merged);
207
209
  enforceOptionsForProduction(merged);
208
210
  // mergeConfigs would mangle functions on the stats class, so do this afterwards
209
- const isLogLevelInfo = [undefined, 'info'].includes(viteConfig.logLevel);
210
- const disableCompileStats = merged.experimental?.disableCompileStats;
211
- const statsEnabled =
212
- disableCompileStats !== true && disableCompileStats !== (merged.isBuild ? 'build' : 'dev');
213
- if (statsEnabled && isLogLevelInfo) {
214
- merged.stats = new VitePluginSvelteStats();
211
+ if (log.debug.enabled && isDebugNamespaceEnabled('stats')) {
212
+ merged.stats = new VitePluginSvelteStats(cache);
215
213
  }
216
214
  return merged;
217
215
  }
@@ -727,11 +725,11 @@ export interface ExperimentalOptions {
727
725
  sendWarningsToBrowser?: boolean;
728
726
 
729
727
  /**
730
- * disable svelte compile statistics
728
+ * disable svelte field resolve warnings
731
729
  *
732
730
  * @default false
733
731
  */
734
- disableCompileStats?: 'dev' | 'build' | boolean;
732
+ disableSvelteResolveWarnings?: boolean;
735
733
  }
736
734
 
737
735
  export interface InspectorOptions {
@@ -1,20 +1,73 @@
1
1
  import path from 'path';
2
+ const IS_WINDOWS = process.platform === 'win32';
3
+ interface SourceMapFileRefs {
4
+ file?: string;
5
+ sources?: string[];
6
+ sourceRoot?: string;
7
+ }
8
+
9
+ /**
10
+ * convert absolute paths in sourcemap file refs to their relative equivalents to avoid leaking fs info
11
+ *
12
+ * map is modified in place.
13
+ *
14
+ * @param map sourcemap
15
+ * @param filename absolute path to file the sourcemap is for
16
+ */
17
+ export function mapToRelative(map: SourceMapFileRefs | undefined, filename: string) {
18
+ if (!map) {
19
+ return;
20
+ }
21
+ const sourceRoot = map.sourceRoot;
22
+ const dirname = path.dirname(filename);
23
+ const toRelative = (s: string) => {
24
+ if (!s) {
25
+ return s;
26
+ }
27
+ let sourcePath: string;
28
+ if (s.startsWith('file:///')) {
29
+ // windows has file:///C:/foo and posix has file:///foo, so we have to remove one extra on windows
30
+ sourcePath = s.slice(IS_WINDOWS ? 8 : 7);
31
+ } else if (sourceRoot) {
32
+ const sep = sourceRoot[sourceRoot.length - 1] === '/' || s[0] === '/' ? '' : '/';
33
+ sourcePath = `${sourceRoot}${sep}${s}`;
34
+ } else {
35
+ sourcePath = s;
36
+ }
37
+ return path.isAbsolute(sourcePath) ? path.relative(dirname, sourcePath) : sourcePath;
38
+ };
39
+
40
+ if (map.file) {
41
+ map.file = path.basename(filename);
42
+ }
43
+ if (map.sources) {
44
+ map.sources = map.sources.map(toRelative);
45
+ }
46
+ if (map.sourceRoot) {
47
+ // we have prepended sourceRoot and computed relative paths from it
48
+ // remove it here to avoid downstream processing prepending it again
49
+ delete map.sourceRoot;
50
+ }
51
+ }
2
52
 
3
53
  /**
4
- * sourcemap sources are relative to the sourcemap itself
5
- * assume the sourcemap location is the same as filename and turn absolute paths to relative
6
- * to avoid leaking fs information like vite root
54
+ * vitePreprocess uses an extra lang extension to tell vite about the type of preprocessor to use
55
+ * This function removes it afterwards to get back working file refs
56
+ *
57
+ * map is modified in place.
58
+ *
59
+ * @param map the output sourcemap
60
+ * @param suffix the suffix to remove
7
61
  */
8
- export function mapSourcesToRelative(map: { sources?: string[] }, filename: string) {
9
- if (map?.sources) {
10
- map.sources = map.sources.map((s) => {
11
- if (path.isAbsolute(s)) {
12
- const relative = path.relative(filename, s);
13
- // empty string as a source is not allowed, use simple filename
14
- return relative === '' ? path.basename(filename) : relative;
15
- } else {
16
- return s;
17
- }
18
- });
62
+ export function removeLangSuffix(map: SourceMapFileRefs | undefined, suffix: string) {
63
+ if (!map) {
64
+ return;
65
+ }
66
+ const removeSuffix = (s: string) => (s?.endsWith(suffix) ? s.slice(0, -1 * suffix.length) : s);
67
+ if (map.file) {
68
+ map.file = removeSuffix(map.file);
69
+ }
70
+ if (map.sources) {
71
+ map.sources = map.sources.map(removeSuffix);
19
72
  }
20
73
  }
@@ -1,5 +1,17 @@
1
1
  import { SvelteRequest } from './id';
2
2
  import { Code, CompileData } from './compile';
3
+ import { readFileSync } from 'fs';
4
+ import { dirname } from 'path';
5
+ //eslint-disable-next-line node/no-missing-import
6
+ import { findClosestPkgJsonPath } from 'vitefu';
7
+ import { normalizePath } from 'vite';
8
+
9
+ interface PackageInfo {
10
+ name: string;
11
+ version: string;
12
+ svelte?: string;
13
+ path: string;
14
+ }
3
15
 
4
16
  export class VitePluginSvelteCache {
5
17
  private _css = new Map<string, Code>();
@@ -8,6 +20,7 @@ export class VitePluginSvelteCache {
8
20
  private _dependants = new Map<string, Set<string>>();
9
21
  private _resolvedSvelteFields = new Map<string, string>();
10
22
  private _errors = new Map<string, any>();
23
+ private _packageInfos: PackageInfo[] = [];
11
24
 
12
25
  public update(compileData: CompileData) {
13
26
  this._errors.delete(compileData.normalizedFilename);
@@ -110,6 +123,9 @@ export class VitePluginSvelteCache {
110
123
  return this._resolvedSvelteFields.get(this._getResolvedSvelteFieldKey(name, importer));
111
124
  }
112
125
 
126
+ public hasResolvedSvelteField(name: string, importer?: string) {
127
+ return this._resolvedSvelteFields.has(this._getResolvedSvelteFieldKey(name, importer));
128
+ }
113
129
  public setResolvedSvelteField(
114
130
  importee: string,
115
131
  importer: string | undefined = undefined,
@@ -124,4 +140,43 @@ export class VitePluginSvelteCache {
124
140
  private _getResolvedSvelteFieldKey(importee: string, importer?: string): string {
125
141
  return importer ? `${importer} > ${importee}` : importee;
126
142
  }
143
+
144
+ public async getPackageInfo(file: string): Promise<PackageInfo> {
145
+ let info = this._packageInfos.find((pi) => file.startsWith(pi.path));
146
+ if (!info) {
147
+ info = await findPackageInfo(file);
148
+ this._packageInfos.push(info);
149
+ }
150
+ return info;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * utility to get some info from the closest package.json with a "name" set
156
+ *
157
+ * @param {string} file to find info for
158
+ * @returns {PackageInfo}
159
+ */
160
+ async function findPackageInfo(file: string): Promise<PackageInfo> {
161
+ const info: PackageInfo = {
162
+ name: '$unknown',
163
+ version: '0.0.0-unknown',
164
+ path: '$unknown'
165
+ };
166
+ let path = await findClosestPkgJsonPath(file, (pkgPath) => {
167
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
168
+ if (pkg.name != null) {
169
+ info.name = pkg.name;
170
+ if (pkg.version != null) {
171
+ info.version = pkg.version;
172
+ }
173
+ info.svelte = pkg.svelte;
174
+ return true;
175
+ }
176
+ return false;
177
+ });
178
+ // return normalized path with appended '/' so .startsWith works for future file checks
179
+ path = normalizePath(dirname(path ?? file)) + '/';
180
+ info.path = path;
181
+ return info;
127
182
  }