@sveltejs/vite-plugin-svelte 1.3.1 → 2.0.0-beta.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,24 +1,19 @@
1
1
  {
2
2
  "name": "@sveltejs/vite-plugin-svelte",
3
- "version": "1.3.1",
3
+ "version": "2.0.0-beta.0",
4
4
  "license": "MIT",
5
5
  "author": "dominikg",
6
6
  "files": [
7
7
  "dist",
8
8
  "src",
9
- "README.md",
10
- "LICENSE",
11
- "package.json"
9
+ "*.d.ts"
12
10
  ],
13
11
  "type": "module",
14
- "main": "dist/index.cjs",
15
- "module": "dist/index.js",
16
12
  "types": "dist/index.d.ts",
17
13
  "exports": {
18
14
  ".": {
19
15
  "types": "./dist/index.d.ts",
20
- "import": "./dist/index.js",
21
- "require": "./dist/index.cjs"
16
+ "import": "./dist/index.js"
22
17
  },
23
18
  "./package.json": "./package.json",
24
19
  "./src/ui/*": "./src/ui/*"
@@ -45,33 +40,25 @@
45
40
  "debug": "^4.3.4",
46
41
  "deepmerge": "^4.2.2",
47
42
  "kleur": "^4.1.5",
48
- "magic-string": "^0.26.7",
43
+ "magic-string": "^0.27.0",
49
44
  "svelte-hmr": "^0.15.1",
50
45
  "vitefu": "^0.2.2"
51
46
  },
52
47
  "peerDependencies": {
53
- "diff-match-patch": "^1.0.5",
54
48
  "svelte": "^3.44.0",
55
- "vite": "^3.0.0"
56
- },
57
- "peerDependenciesMeta": {
58
- "diff-match-patch": {
59
- "optional": true
60
- }
49
+ "vite": "^4.0.0-beta.1"
61
50
  },
62
51
  "devDependencies": {
63
52
  "@types/debug": "^4.1.7",
64
- "@types/diff-match-patch": "^1.0.32",
65
- "diff-match-patch": "^1.0.5",
66
- "esbuild": "^0.15.14",
53
+ "esbuild": "^0.15.18",
67
54
  "rollup": "^2.79.1",
68
55
  "svelte": "^3.53.1",
69
56
  "tsup": "^6.5.0",
70
- "vite": "^3.2.3"
57
+ "vite": "^4.0.0-beta.1"
71
58
  },
72
59
  "scripts": {
73
60
  "dev": "pnpm build:ci --sourcemap --watch src",
74
- "build:ci": "rimraf dist && tsup-node src/index.ts --format esm,cjs --no-splitting --shims",
61
+ "build:ci": "rimraf dist && tsup-node src/index.ts --format esm",
75
62
  "build": "pnpm build:ci --dts --sourcemap"
76
63
  }
77
64
  }
@@ -20,7 +20,7 @@ export async function handleHotUpdate(
20
20
  log.debug(`handleHotUpdate called before initial transform for ${svelteRequest.id}`);
21
21
  return;
22
22
  }
23
- const { read, server } = ctx;
23
+ const { read, server, modules } = ctx;
24
24
 
25
25
  const cachedJS = cache.getJS(svelteRequest);
26
26
  const cachedCss = cache.getCSS(svelteRequest);
@@ -35,41 +35,41 @@ export async function handleHotUpdate(
35
35
  throw e;
36
36
  }
37
37
 
38
- const affectedModules = new Set<ModuleNode | undefined>();
38
+ const affectedModules = [...modules];
39
39
 
40
- const cssModule = server.moduleGraph.getModuleById(svelteRequest.cssId);
41
- const mainModule = server.moduleGraph.getModuleById(svelteRequest.id);
42
- const cssUpdated = cssModule && cssChanged(cachedCss, compileData.compiled.css);
43
- if (cssUpdated) {
44
- log.debug(`handleHotUpdate css changed for ${svelteRequest.cssId}`);
45
- affectedModules.add(cssModule);
40
+ const cssIdx = modules.findIndex((m) => m.id === svelteRequest.cssId);
41
+ if (cssIdx > -1) {
42
+ const cssUpdated = cssChanged(cachedCss, compileData.compiled.css);
43
+ if (!cssUpdated) {
44
+ log.debug(`skipping unchanged css for ${svelteRequest.cssId}`);
45
+ affectedModules.splice(cssIdx, 1);
46
+ }
46
47
  }
47
- const jsUpdated =
48
- mainModule && jsChanged(cachedJS, compileData.compiled.js, svelteRequest.filename);
49
- if (jsUpdated) {
50
- log.debug(`handleHotUpdate js changed for ${svelteRequest.id}`);
51
- affectedModules.add(mainModule);
48
+ const jsIdx = modules.findIndex((m) => m.id === svelteRequest.id);
49
+ if (jsIdx > -1) {
50
+ const jsUpdated = jsChanged(cachedJS, compileData.compiled.js, svelteRequest.filename);
51
+ if (!jsUpdated) {
52
+ log.debug(`skipping unchanged js for ${svelteRequest.id}`);
53
+ affectedModules.splice(jsIdx, 1);
54
+ // transform won't be called, log warnings here
55
+ logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
56
+ }
52
57
  }
53
58
 
54
- if (!jsUpdated) {
55
- // transform won't be called, log warnings here
56
- logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
57
- }
58
-
59
- const result = [...affectedModules].filter(Boolean) as ModuleNode[];
60
-
61
59
  // TODO is this enough? see also: https://github.com/vitejs/vite/issues/2274
62
- const ssrModulesToInvalidate = result.filter((m) => !!m.ssrTransformResult);
60
+ const ssrModulesToInvalidate = affectedModules.filter((m) => !!m.ssrTransformResult);
63
61
  if (ssrModulesToInvalidate.length > 0) {
64
62
  log.debug(`invalidating modules ${ssrModulesToInvalidate.map((m) => m.id).join(', ')}`);
65
63
  ssrModulesToInvalidate.forEach((moduleNode) => server.moduleGraph.invalidateModule(moduleNode));
66
64
  }
67
- if (result.length > 0) {
65
+ if (affectedModules.length > 0) {
68
66
  log.debug(
69
- `handleHotUpdate for ${svelteRequest.id} result: ${result.map((m) => m.id).join(', ')}`
67
+ `handleHotUpdate for ${svelteRequest.id} result: ${affectedModules
68
+ .map((m) => m.id)
69
+ .join(', ')}`
70
70
  );
71
71
  }
72
- return result;
72
+ return affectedModules;
73
73
  }
74
74
 
75
75
  function cssChanged(prev?: Code, next?: Code): boolean {
package/src/index.ts CHANGED
@@ -4,8 +4,8 @@ import { HmrContext, ModuleNode, Plugin, ResolvedConfig, UserConfig } from 'vite
4
4
  import { isDepExcluded } from 'vitefu';
5
5
  import { handleHotUpdate } from './handle-hot-update';
6
6
  import { log, logCompilerWarnings } from './utils/log';
7
- import { CompileData, createCompileSvelte } from './utils/compile';
8
- import { buildIdParser, IdParser, SvelteRequest } from './utils/id';
7
+ import { type CompileSvelte, createCompileSvelte } from './utils/compile';
8
+ import { buildIdParser, IdParser } from './utils/id';
9
9
  import {
10
10
  buildExtraViteConfig,
11
11
  validateInlineOptions,
@@ -23,6 +23,7 @@ import { toRollupError } from './utils/error';
23
23
  import { saveSvelteMetadata } from './utils/optimizer';
24
24
  import { svelteInspector } from './ui/inspector/plugin';
25
25
  import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache';
26
+ import { loadRaw } from './utils/load-raw';
26
27
 
27
28
  interface PluginAPI {
28
29
  /**
@@ -45,11 +46,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
45
46
  let options: ResolvedOptions;
46
47
  let viteConfig: ResolvedConfig;
47
48
  /* eslint-disable no-unused-vars */
48
- let compileSvelte: (
49
- svelteRequest: SvelteRequest,
50
- code: string,
51
- options: Partial<ResolvedOptions>
52
- ) => Promise<CompileData>;
49
+ let compileSvelte: CompileSvelte;
53
50
  /* eslint-enable no-unused-vars */
54
51
 
55
52
  let resolvedSvelteSSR: Promise<PartialResolvedId | null>;
@@ -103,23 +100,26 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
103
100
  setupWatchers(options, cache, requestParser);
104
101
  },
105
102
 
106
- load(id, opts) {
103
+ async load(id, opts) {
107
104
  const ssr = !!opts?.ssr;
108
105
  const svelteRequest = requestParser(id, !!ssr);
109
106
  if (svelteRequest) {
110
- const { filename, query } = svelteRequest;
111
- // virtual css module
112
- if (query.svelte && query.type === 'style') {
113
- const css = cache.getCSS(svelteRequest);
114
- if (css) {
115
- log.debug(`load returns css for ${filename}`);
116
- return css;
107
+ const { filename, query, raw } = svelteRequest;
108
+ if (raw) {
109
+ return loadRaw(svelteRequest, compileSvelte, options);
110
+ } else {
111
+ if (query.svelte && query.type === 'style') {
112
+ const css = cache.getCSS(svelteRequest);
113
+ if (css) {
114
+ log.debug(`load returns css for ${filename}`);
115
+ return css;
116
+ }
117
+ }
118
+ // prevent vite asset plugin from loading files as url that should be compiled in transform
119
+ if (viteConfig.assetsInclude(filename)) {
120
+ log.debug(`load returns raw content for ${filename}`);
121
+ return fs.readFileSync(filename, 'utf-8');
117
122
  }
118
- }
119
- // prevent vite asset plugin from loading files as url that should be compiled in transform
120
- if (viteConfig.assetsInclude(filename)) {
121
- log.debug(`load returns raw content for ${filename}`);
122
- return fs.readFileSync(filename, 'utf-8');
123
123
  }
124
124
  }
125
125
  },
@@ -128,14 +128,12 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
128
128
  const ssr = !!opts?.ssr;
129
129
  const svelteRequest = requestParser(importee, ssr);
130
130
  if (svelteRequest?.query.svelte) {
131
- if (svelteRequest.query.type === 'style') {
131
+ if (svelteRequest.query.type === 'style' && !svelteRequest.raw) {
132
132
  // return cssId with root prefix so postcss pipeline of vite finds the directory correctly
133
133
  // see https://github.com/sveltejs/vite-plugin-svelte/issues/14
134
134
  log.debug(`resolveId resolved virtual css module ${svelteRequest.cssId}`);
135
135
  return svelteRequest.cssId;
136
136
  }
137
- log.debug(`resolveId resolved ${importee}`);
138
- return importee; // query with svelte tag, an id we generated, no need for further analysis
139
137
  }
140
138
 
141
139
  if (ssr && importee === 'svelte') {
@@ -188,7 +186,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
188
186
  async transform(code, id, opts) {
189
187
  const ssr = !!opts?.ssr;
190
188
  const svelteRequest = requestParser(id, ssr);
191
- if (!svelteRequest || svelteRequest.query.svelte) {
189
+ if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) {
192
190
  return;
193
191
  }
194
192
  let compileData;
@@ -238,6 +236,7 @@ export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
238
236
  return plugins.filter(Boolean);
239
237
  }
240
238
 
239
+ export { vitePreprocess } from './preprocess';
241
240
  export { loadSvelteConfig } from './utils/load-svelte-config';
242
241
 
243
242
  export {
@@ -0,0 +1,114 @@
1
+ import path from 'path';
2
+ import * as vite from 'vite';
3
+ import type { ESBuildOptions, ResolvedConfig } from 'vite';
4
+ // eslint-disable-next-line node/no-missing-import
5
+ import type { Preprocessor, PreprocessorGroup } from 'svelte/types/compiler/preprocess';
6
+
7
+ const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss', 'sss'];
8
+ const supportedScriptLangs = ['ts'];
9
+
10
+ export function vitePreprocess(opts?: {
11
+ script?: boolean;
12
+ style?: boolean | vite.InlineConfig | vite.ResolvedConfig;
13
+ }) {
14
+ const preprocessor: PreprocessorGroup = {};
15
+ if (opts?.script !== false) {
16
+ preprocessor.script = viteScript().script;
17
+ }
18
+ if (opts?.style !== false) {
19
+ const styleOpts = typeof opts?.style == 'object' ? opts?.style : undefined;
20
+ preprocessor.style = viteStyle(styleOpts).style;
21
+ }
22
+ return preprocessor;
23
+ }
24
+
25
+ function viteScript(): { script: Preprocessor } {
26
+ return {
27
+ async script({ attributes, content, filename = '' }) {
28
+ const lang = attributes.lang as string;
29
+ if (!supportedScriptLangs.includes(lang)) return;
30
+ const transformResult = await vite.transformWithEsbuild(content, filename, {
31
+ loader: lang as ESBuildOptions['loader'],
32
+ target: 'esnext',
33
+ tsconfigRaw: {
34
+ compilerOptions: {
35
+ // svelte typescript needs this flag to work with type imports
36
+ importsNotUsedAsValues: 'preserve',
37
+ preserveValueImports: true
38
+ }
39
+ }
40
+ });
41
+ return {
42
+ code: transformResult.code,
43
+ map: transformResult.map
44
+ };
45
+ }
46
+ };
47
+ }
48
+
49
+ function viteStyle(config: vite.InlineConfig | vite.ResolvedConfig = {}): {
50
+ style: Preprocessor;
51
+ } {
52
+ let transform: CssTransform;
53
+ const style: Preprocessor = async ({ attributes, content, filename = '' }) => {
54
+ const lang = attributes.lang as string;
55
+ if (!supportedStyleLangs.includes(lang)) return;
56
+ if (!transform) {
57
+ let resolvedConfig: vite.ResolvedConfig;
58
+ // @ts-expect-error special prop added if running in v-p-s
59
+ if (style.__resolvedConfig) {
60
+ // @ts-expect-error
61
+ resolvedConfig = style.__resolvedConfig;
62
+ } else if (isResolvedConfig(config)) {
63
+ resolvedConfig = config;
64
+ } else {
65
+ resolvedConfig = await vite.resolveConfig(
66
+ config,
67
+ process.env.NODE_ENV === 'production' ? 'build' : 'serve'
68
+ );
69
+ }
70
+ transform = getCssTransformFn(resolvedConfig);
71
+ }
72
+ const moduleId = `${filename}.${lang}`;
73
+ const result = await transform(content, moduleId);
74
+ // patch sourcemap source to point back to original filename
75
+ if (result.map?.sources?.[0] === moduleId) {
76
+ result.map.sources[0] = path.basename(filename);
77
+ }
78
+ return {
79
+ code: result.code,
80
+ map: result.map ?? undefined
81
+ };
82
+ };
83
+ // @ts-expect-error tag so can be found by v-p-s
84
+ style.__resolvedConfig = null;
85
+ return { style };
86
+ }
87
+
88
+ // eslint-disable-next-line no-unused-vars
89
+ type CssTransform = (code: string, filename: string) => Promise<{ code: string; map?: any }>;
90
+
91
+ function getCssTransformFn(config: ResolvedConfig): CssTransform {
92
+ // API is only available in Vite 3.2 and above
93
+ // TODO: Remove Vite plugin hack when bump peer dep to Vite 3.2
94
+ if (vite.preprocessCSS) {
95
+ return async (code, filename) => {
96
+ return vite.preprocessCSS(code, filename, config);
97
+ };
98
+ } else {
99
+ const pluginName = 'vite:css';
100
+ const plugin = config.plugins.find((p) => p.name === pluginName);
101
+ if (!plugin) {
102
+ throw new Error(`failed to find plugin ${pluginName}`);
103
+ }
104
+ if (!plugin.transform) {
105
+ throw new Error(`plugin ${pluginName} has no transform`);
106
+ }
107
+ // @ts-expect-error
108
+ return plugin.transform.bind(null);
109
+ }
110
+ }
111
+
112
+ function isResolvedConfig(config: any): config is vite.ResolvedConfig {
113
+ return !!config.inlineConfig;
114
+ }
@@ -6,17 +6,41 @@ import { SvelteRequest } from './id';
6
6
  import { safeBase64Hash } from './hash';
7
7
  import { log } from './log';
8
8
  import { StatCollection } from './vite-plugin-svelte-stats';
9
+ //eslint-disable-next-line node/no-missing-import
10
+ import type { Processed } from 'svelte/types/compiler/preprocess';
11
+ import { createInjectScopeEverythingRulePreprocessorGroup } from './preprocess';
12
+ import path from 'path';
9
13
 
10
14
  const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/;
11
15
 
16
+ export type CompileSvelte = ReturnType<typeof _createCompileSvelte>;
17
+
18
+ function mapSourcesToRelative(map: { sources?: string[] }, filename: string) {
19
+ // sourcemap sources are relative to the sourcemap itself
20
+ // assume the sourcemap location is the same as filename and turn absolute paths to relative
21
+ // to avoid leaking fs information like vite root
22
+ if (map?.sources) {
23
+ map.sources = map.sources.map((s) => {
24
+ if (path.isAbsolute(s)) {
25
+ const relative = path.relative(filename, s);
26
+ // empty string a source is not allowed, use simple filename
27
+ return relative === '' ? path.basename(filename) : relative;
28
+ } else {
29
+ return s;
30
+ }
31
+ });
32
+ }
33
+ }
34
+
12
35
  const _createCompileSvelte = (makeHot: Function) => {
13
36
  let stats: StatCollection | undefined;
37
+ const devStylePreprocessor = createInjectScopeEverythingRulePreprocessorGroup();
14
38
  return async function compileSvelte(
15
39
  svelteRequest: SvelteRequest,
16
40
  code: string,
17
41
  options: Partial<ResolvedOptions>
18
42
  ): Promise<CompileData> {
19
- const { filename, normalizedFilename, cssId, ssr } = svelteRequest;
43
+ const { filename, normalizedFilename, cssId, ssr, raw } = svelteRequest;
20
44
  const { emitCss = true } = options;
21
45
  const dependencies = [];
22
46
 
@@ -47,7 +71,7 @@ const _createCompileSvelte = (makeHot: Function) => {
47
71
 
48
72
  const compileOptions: CompileOptions = {
49
73
  ...options.compilerOptions,
50
- filename,
74
+ filename: normalizedFilename, // use normalized here to avoid bleeding absolute fs path
51
75
  generate: ssr ? 'ssr' : 'dom',
52
76
  format: 'esm'
53
77
  };
@@ -65,10 +89,20 @@ const _createCompileSvelte = (makeHot: Function) => {
65
89
  }
66
90
 
67
91
  let preprocessed;
68
-
69
- if (options.preprocess) {
92
+ let preprocessors = options.preprocess;
93
+ if (!options.isBuild && options.emitCss && options.hot) {
94
+ // inject preprocessor that ensures css hmr works better
95
+ if (!Array.isArray(preprocessors)) {
96
+ preprocessors = preprocessors
97
+ ? [preprocessors, devStylePreprocessor]
98
+ : [devStylePreprocessor];
99
+ } else {
100
+ preprocessors = preprocessors.concat(devStylePreprocessor);
101
+ }
102
+ }
103
+ if (preprocessors) {
70
104
  try {
71
- preprocessed = await preprocess(code, options.preprocess, { filename });
105
+ preprocessed = await preprocess(code, preprocessors, { filename }); // full filename here so postcss works
72
106
  } catch (e) {
73
107
  e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
74
108
  throw e;
@@ -77,6 +111,13 @@ const _createCompileSvelte = (makeHot: Function) => {
77
111
  if (preprocessed.dependencies) dependencies.push(...preprocessed.dependencies);
78
112
  if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
79
113
  }
114
+ if (typeof preprocessed?.map === 'object') {
115
+ mapSourcesToRelative(preprocessed?.map, filename);
116
+ }
117
+ if (raw && svelteRequest.query.type === 'preprocessed') {
118
+ // shortcut
119
+ return { preprocessed: preprocessed ?? { code } } as CompileData;
120
+ }
80
121
  const finalCode = preprocessed ? preprocessed.code : code;
81
122
  const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
82
123
  filename,
@@ -100,24 +141,29 @@ const _createCompileSvelte = (makeHot: Function) => {
100
141
  if (endStat) {
101
142
  endStat();
102
143
  }
144
+ mapSourcesToRelative(compiled.js?.map, filename);
145
+ mapSourcesToRelative(compiled.css?.map, filename);
146
+ if (!raw) {
147
+ // wire css import and code for hmr
148
+ const hasCss = compiled.css?.code?.trim().length > 0;
149
+ // compiler might not emit css with mode none or it may be empty
150
+ if (emitCss && hasCss) {
151
+ // TODO properly update sourcemap?
152
+ compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`;
153
+ }
103
154
 
104
- const hasCss = compiled.css?.code?.trim().length > 0;
105
- // compiler might not emit css with mode none or it may be empty
106
- if (emitCss && hasCss) {
107
- // TODO properly update sourcemap?
108
- compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`;
109
- }
110
-
111
- // only apply hmr when not in ssr context and hot options are set
112
- if (!ssr && makeHot) {
113
- compiled.js.code = makeHot({
114
- id: filename,
115
- compiledCode: compiled.js.code,
116
- hotOptions: { ...options.hot, injectCss: options.hot?.injectCss === true && hasCss },
117
- compiled,
118
- originalCode: code,
119
- compileOptions: finalCompileOptions
120
- });
155
+ // only apply hmr when not in ssr context and hot options are set
156
+ if (!ssr && makeHot) {
157
+ compiled.js.code = makeHot({
158
+ id: filename,
159
+ compiledCode: compiled.js.code,
160
+ //@ts-expect-error hot isn't a boolean at this point
161
+ hotOptions: { ...options.hot, injectCss: options.hot?.injectCss === true && hasCss },
162
+ compiled,
163
+ originalCode: code,
164
+ compileOptions: finalCompileOptions
165
+ });
166
+ }
121
167
  }
122
168
 
123
169
  compiled.js.dependencies = dependencies;
@@ -129,7 +175,8 @@ const _createCompileSvelte = (makeHot: Function) => {
129
175
  // @ts-ignore
130
176
  compiled,
131
177
  ssr,
132
- dependencies
178
+ dependencies,
179
+ preprocessed: preprocessed ?? { code }
133
180
  };
134
181
  };
135
182
  };
@@ -190,4 +237,5 @@ export interface CompileData {
190
237
  compiled: Compiled;
191
238
  ssr: boolean | undefined;
192
239
  dependencies: string[];
240
+ preprocessed: Processed;
193
241
  }
package/src/utils/id.ts CHANGED
@@ -3,19 +3,39 @@ import { createFilter } from 'vite';
3
3
  import { Arrayable, ResolvedOptions } from './options';
4
4
  import { normalizePath } from 'vite';
5
5
  import * as fs from 'fs';
6
+ //eslint-disable-next-line node/no-missing-import
7
+ import { CompileOptions } from 'svelte/types/compiler/interfaces';
8
+ import { log } from './log';
6
9
 
7
10
  const VITE_FS_PREFIX = '/@fs/';
8
11
  const IS_WINDOWS = process.platform === 'win32';
9
12
 
10
- export type SvelteQueryTypes = 'style' | 'script';
13
+ const SUPPORTED_COMPILER_OPTIONS = [
14
+ 'generate',
15
+ 'dev',
16
+ 'css',
17
+ 'hydratable',
18
+ 'customElement',
19
+ 'immutable',
20
+ 'enableSourcemap'
21
+ ];
22
+ const TYPES_WITH_COMPILER_OPTIONS = ['style', 'script', 'all'];
23
+
24
+ export type SvelteQueryTypes = 'style' | 'script' | 'preprocessed' | 'all';
11
25
 
12
26
  export interface RequestQuery {
13
27
  // our own
14
28
  svelte?: boolean;
15
29
  type?: SvelteQueryTypes;
30
+ sourcemap?: boolean;
31
+ compilerOptions?: Pick<
32
+ CompileOptions,
33
+ 'generate' | 'dev' | 'css' | 'hydratable' | 'customElement' | 'immutable' | 'enableSourcemap'
34
+ >;
16
35
  // vite specific
17
36
  url?: boolean;
18
37
  raw?: boolean;
38
+ direct?: boolean;
19
39
  }
20
40
 
21
41
  export interface SvelteRequest {
@@ -26,6 +46,7 @@ export interface SvelteRequest {
26
46
  query: RequestQuery;
27
47
  timestamp: number;
28
48
  ssr: boolean;
49
+ raw: boolean;
29
50
  }
30
51
 
31
52
  function splitId(id: string) {
@@ -44,10 +65,12 @@ function parseToSvelteRequest(
44
65
  ssr: boolean
45
66
  ): SvelteRequest | undefined {
46
67
  const query = parseRequestQuery(rawQuery);
47
- if (query.url || query.raw) {
68
+ const rawOrDirect = !!(query.raw || query.direct);
69
+ if (query.url || (!query.svelte && rawOrDirect)) {
48
70
  // skip requests with special vite tags
49
71
  return;
50
72
  }
73
+ const raw = rawOrDirect;
51
74
  const normalizedFilename = normalize(filename, root);
52
75
  const cssId = createVirtualImportId(filename, root, 'style');
53
76
 
@@ -58,7 +81,8 @@ function parseToSvelteRequest(
58
81
  cssId,
59
82
  query,
60
83
  timestamp,
61
- ssr
84
+ ssr,
85
+ raw
62
86
  };
63
87
  }
64
88
 
@@ -86,6 +110,34 @@ function parseRequestQuery(rawQuery: string): RequestQuery {
86
110
  query[key] = true;
87
111
  }
88
112
  }
113
+ const compilerOptions = query.compilerOptions;
114
+ if (compilerOptions) {
115
+ if (!((query.raw || query.direct) && TYPES_WITH_COMPILER_OPTIONS.includes(query.type))) {
116
+ throw new Error(
117
+ `Invalid compilerOptions in query ${rawQuery}. CompilerOptions are only supported for raw or direct queries with type in "${TYPES_WITH_COMPILER_OPTIONS.join(
118
+ ', '
119
+ )}" e.g. '?svelte&raw&type=script&compilerOptions={"generate":"ssr","dev":false}`
120
+ );
121
+ }
122
+ try {
123
+ const parsed = JSON.parse(compilerOptions);
124
+ const invalid = Object.keys(parsed).filter(
125
+ (key) => !SUPPORTED_COMPILER_OPTIONS.includes(key)
126
+ );
127
+ if (invalid.length) {
128
+ throw new Error(
129
+ `Invalid compilerOptions in query ${rawQuery}: ${invalid.join(
130
+ ', '
131
+ )}. Supported: ${SUPPORTED_COMPILER_OPTIONS.join(', ')}`
132
+ );
133
+ }
134
+ query.compilerOptions = parsed;
135
+ } catch (e) {
136
+ log.error('failed to parse request query compilerOptions', e);
137
+ throw e;
138
+ }
139
+ }
140
+
89
141
  return query as RequestQuery;
90
142
  }
91
143