@sveltejs/vite-plugin-svelte 1.3.0 → 1.4.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.
@@ -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
 
@@ -0,0 +1,132 @@
1
+ import { ResolvedOptions } from './options';
2
+ import fs from 'fs';
3
+ import { toRollupError } from './error';
4
+ import { log } from './log';
5
+ import type { SvelteRequest } from './id';
6
+ import { type CompileData, CompileSvelte } from './compile';
7
+
8
+ /**
9
+ * utility function to compile ?raw and ?direct requests in load hook
10
+ */
11
+ export async function loadRaw(
12
+ svelteRequest: SvelteRequest,
13
+ compileSvelte: CompileSvelte,
14
+ options: ResolvedOptions
15
+ ) {
16
+ const { id, filename, query } = svelteRequest;
17
+
18
+ // raw svelte subrequest, compile on the fly and return requested subpart
19
+ let compileData;
20
+ const source = fs.readFileSync(filename, 'utf-8');
21
+ try {
22
+ //avoid compileSvelte doing extra ssr stuff unless requested
23
+ svelteRequest.ssr = query.compilerOptions?.generate === 'ssr';
24
+ const type = query.type;
25
+ compileData = await compileSvelte(svelteRequest, source, {
26
+ ...options,
27
+ // don't use dynamic vite-plugin-svelte defaults here to ensure stable result between ssr,dev and build
28
+ compilerOptions: {
29
+ dev: false,
30
+ css: false,
31
+ hydratable: false,
32
+ enableSourcemap: query.sourcemap
33
+ ? {
34
+ js: type === 'script' || type === 'all',
35
+ css: type === 'style' || type === 'all'
36
+ }
37
+ : false,
38
+ ...svelteRequest.query.compilerOptions
39
+ },
40
+ hot: false,
41
+ emitCss: true
42
+ });
43
+ } catch (e) {
44
+ throw toRollupError(e, options);
45
+ }
46
+ let result;
47
+ if (query.type === 'style') {
48
+ result = compileData.compiled.css;
49
+ } else if (query.type === 'script') {
50
+ result = compileData.compiled.js;
51
+ } else if (query.type === 'preprocessed') {
52
+ result = compileData.preprocessed;
53
+ } else if (query.type === 'all' && query.raw) {
54
+ return allToRawExports(compileData, source);
55
+ } else {
56
+ throw new Error(
57
+ `invalid "type=${query.type}" in ${id}. supported are script, style, preprocessed, all`
58
+ );
59
+ }
60
+ if (query.direct) {
61
+ const supportedDirectTypes = ['script', 'style'];
62
+ if (!supportedDirectTypes.includes(query.type)) {
63
+ throw new Error(
64
+ `invalid "type=${
65
+ query.type
66
+ }" combined with direct in ${id}. supported are: ${supportedDirectTypes.join(', ')}`
67
+ );
68
+ }
69
+ log.debug(`load returns direct result for ${id}`);
70
+ let directOutput = result.code;
71
+ if (query.sourcemap && result.map?.toUrl) {
72
+ const map = `sourceMappingURL=${result.map.toUrl()}`;
73
+ if (query.type === 'style') {
74
+ directOutput += `\n\n/*# ${map} */\n`;
75
+ } else if (query.type === 'script') {
76
+ directOutput += `\n\n//# ${map}\n`;
77
+ }
78
+ }
79
+ return directOutput;
80
+ } else if (query.raw) {
81
+ log.debug(`load returns raw result for ${id}`);
82
+ return toRawExports(result);
83
+ } else {
84
+ throw new Error(`invalid raw mode in ${id}, supported are raw, direct`);
85
+ }
86
+ }
87
+
88
+ /**
89
+ * turn compileData and source into a flat list of raw exports
90
+ *
91
+ * @param compileData
92
+ * @param source
93
+ */
94
+ function allToRawExports(compileData: CompileData, source: string) {
95
+ // flatten CompileData
96
+ const exports: Partial<CompileData & { source: string }> = {
97
+ ...compileData,
98
+ ...compileData.compiled,
99
+ source
100
+ };
101
+ delete exports.compiled;
102
+ delete exports.filename; // absolute path, remove to avoid it in output
103
+ return toRawExports(exports);
104
+ }
105
+
106
+ /**
107
+ * turn object into raw exports.
108
+ *
109
+ * every prop is returned as a const export, and if prop 'code' exists it is additionally added as default export
110
+ *
111
+ * eg {'foo':'bar','code':'baz'} results in
112
+ *
113
+ * ```js
114
+ * export const code='baz'
115
+ * export const foo='bar'
116
+ * export default code
117
+ * ```
118
+ * @param object
119
+ */
120
+ function toRawExports(object: object) {
121
+ let exports =
122
+ Object.entries(object)
123
+ //eslint-disable-next-line no-unused-vars
124
+ .filter(([key, value]) => typeof value !== 'function') // preprocess output has a toString function that's enumerable
125
+ .sort(([a], [b]) => (a < b ? -1 : a === b ? 0 : 1))
126
+ .map(([key, value]) => `export const ${key}=${JSON.stringify(value)}`)
127
+ .join('\n') + '\n';
128
+ if (Object.prototype.hasOwnProperty.call(object, 'code')) {
129
+ exports += `export default code\n`;
130
+ }
131
+ return exports;
132
+ }
@@ -315,6 +315,9 @@ function handleDeprecatedOptions(options: ResolvedOptions) {
315
315
  'experimental.prebundleSvelteLibraries is no longer experimental and has moved to prebundleSvelteLibraries'
316
316
  );
317
317
  }
318
+ if ((options.experimental as any)?.generateMissingPreprocessorSourcemaps) {
319
+ log.warn('experimental.generateMissingPreprocessorSourcemaps has been removed.');
320
+ }
318
321
  }
319
322
 
320
323
  // vite passes unresolved `root`option to config hook but we need the resolved value, so do it here
@@ -532,6 +535,15 @@ function buildExtraConfigForSvelte(config: UserConfig) {
532
535
  }
533
536
 
534
537
  export function patchResolvedViteConfig(viteConfig: ResolvedConfig, options: ResolvedOptions) {
538
+ if (options.preprocess) {
539
+ for (const preprocessor of arraify(options.preprocess)) {
540
+ if (preprocessor.style && '__resolvedConfig' in preprocessor.style) {
541
+ preprocessor.style.__resolvedConfig = viteConfig;
542
+ }
543
+ }
544
+ }
545
+
546
+ // replace facade esbuild plugin with a real one
535
547
  const facadeEsbuildSveltePlugin = viteConfig.optimizeDeps.esbuildOptions?.plugins?.find(
536
548
  (plugin) => plugin.name === facadeEsbuildSveltePluginName
537
549
  );
@@ -540,6 +552,10 @@ export function patchResolvedViteConfig(viteConfig: ResolvedConfig, options: Res
540
552
  }
541
553
  }
542
554
 
555
+ function arraify<T>(value: T | T[]): T[] {
556
+ return Array.isArray(value) ? value : [value];
557
+ }
558
+
543
559
  export type Options = Omit<SvelteOptions, 'vitePlugin'> & PluginOptionsInline;
544
560
 
545
561
  interface PluginOptionsInline extends PluginOptions {
@@ -680,15 +696,6 @@ export interface ExperimentalOptions {
680
696
  */
681
697
  useVitePreprocess?: boolean;
682
698
 
683
- /**
684
- * If a preprocessor does not provide a sourcemap, a best-effort fallback sourcemap will be provided.
685
- * This option requires `diff-match-patch` to be installed as a peer dependency.
686
- *
687
- * @see https://github.com/google/diff-match-patch
688
- * @default false
689
- */
690
- generateMissingPreprocessorSourcemaps?: boolean;
691
-
692
699
  /**
693
700
  * A function to update `compilerOptions` before compilation
694
701
  *
@@ -1,93 +1,17 @@
1
- import * as vite from 'vite';
2
- import type { ESBuildOptions, ResolvedConfig, Plugin } from 'vite';
1
+ import type { ResolvedConfig, Plugin } from 'vite';
3
2
  import MagicString from 'magic-string';
4
3
  import { preprocess } from 'svelte/compiler';
5
- import { Preprocessor, PreprocessorGroup, Processed, ResolvedOptions } from './options';
4
+ import { PreprocessorGroup, ResolvedOptions } from './options';
6
5
  import { log } from './log';
7
- import { buildSourceMap } from './sourcemap';
8
6
  import path from 'path';
9
-
10
- const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss'];
11
-
12
- const supportedScriptLangs = ['ts'];
13
-
14
- function createViteScriptPreprocessor(): Preprocessor {
15
- return async ({ attributes, content, filename = '' }) => {
16
- const lang = attributes.lang as string;
17
- if (!supportedScriptLangs.includes(lang)) return;
18
- const transformResult = await vite.transformWithEsbuild(content, filename, {
19
- loader: lang as ESBuildOptions['loader'],
20
- target: 'esnext',
21
- tsconfigRaw: {
22
- compilerOptions: {
23
- // svelte typescript needs this flag to work with type imports
24
- importsNotUsedAsValues: 'preserve',
25
- preserveValueImports: true
26
- }
27
- }
28
- });
29
- return {
30
- code: transformResult.code,
31
- map: transformResult.map
32
- };
33
- };
34
- }
35
-
36
- function createViteStylePreprocessor(config: ResolvedConfig): Preprocessor {
37
- const transform = getCssTransformFn(config);
38
- return async ({ attributes, content, filename = '' }) => {
39
- const lang = attributes.lang as string;
40
- if (!supportedStyleLangs.includes(lang)) return;
41
- const moduleId = `${filename}.${lang}`;
42
- const result = await transform(content, moduleId);
43
- // patch sourcemap source to point back to original filename
44
- if (result.map?.sources?.[0] === moduleId) {
45
- result.map.sources[0] = path.basename(filename);
46
- }
47
- return {
48
- code: result.code,
49
- map: result.map ?? undefined
50
- };
51
- };
52
- }
53
-
54
- // eslint-disable-next-line no-unused-vars
55
- type CssTransform = (code: string, filename: string) => Promise<{ code: string; map?: any }>;
56
-
57
- function getCssTransformFn(config: ResolvedConfig): CssTransform {
58
- // API is only available in Vite 3.2 and above
59
- // TODO: Remove Vite plugin hack when bump peer dep to Vite 3.2
60
- if (vite.preprocessCSS) {
61
- return async (code, filename) => {
62
- return vite.preprocessCSS(code, filename, config);
63
- };
64
- } else {
65
- const pluginName = 'vite:css';
66
- const plugin = config.plugins.find((p) => p.name === pluginName);
67
- if (!plugin) {
68
- throw new Error(`failed to find plugin ${pluginName}`);
69
- }
70
- if (!plugin.transform) {
71
- throw new Error(`plugin ${pluginName} has no transform`);
72
- }
73
- // @ts-expect-error
74
- return plugin.transform.bind(null);
75
- }
76
- }
7
+ import { vitePreprocess } from '../preprocess';
77
8
 
78
9
  function createVitePreprocessorGroup(config: ResolvedConfig): PreprocessorGroup {
79
10
  return {
80
11
  markup({ content, filename }) {
81
- return preprocess(
82
- content,
83
- {
84
- script: createViteScriptPreprocessor(),
85
- style: createViteStylePreprocessor(config)
86
- },
87
- { filename }
88
- );
12
+ return preprocess(content, vitePreprocess({ style: config }), { filename });
89
13
  }
90
- } as PreprocessorGroup;
14
+ };
91
15
  }
92
16
 
93
17
  /**
@@ -96,7 +20,7 @@ function createVitePreprocessorGroup(config: ResolvedConfig): PreprocessorGroup
96
20
  *
97
21
  * only used during dev with enabled css hmr
98
22
  */
99
- function createInjectScopeEverythingRulePreprocessorGroup(): PreprocessorGroup {
23
+ export function createInjectScopeEverythingRulePreprocessorGroup(): PreprocessorGroup {
100
24
  return {
101
25
  style({ content, filename }) {
102
26
  const s = new MagicString(content);
@@ -117,7 +41,9 @@ function buildExtraPreprocessors(options: ResolvedOptions, config: ResolvedConfi
117
41
  const appendPreprocessors: PreprocessorGroup[] = [];
118
42
 
119
43
  if (options.experimental?.useVitePreprocess) {
120
- log.debug('adding vite preprocessor');
44
+ log.warn(
45
+ '`experimental.useVitePreprocess` is deprecated. Use the `vitePreprocess()` preprocessor instead. See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/preprocess.md for more information.'
46
+ );
121
47
  prependPreprocessors.push(createVitePreprocessorGroup(config));
122
48
  }
123
49
 
@@ -175,10 +101,6 @@ function buildExtraPreprocessors(options: ResolvedOptions, config: ResolvedConfi
175
101
  appendPreprocessors.push(...pluginsWithPreprocessors.map((p) => p.api.sveltePreprocess));
176
102
  }
177
103
 
178
- if (options.hot && options.emitCss) {
179
- appendPreprocessors.push(createInjectScopeEverythingRulePreprocessorGroup());
180
- }
181
-
182
104
  return { prependPreprocessors, appendPreprocessors };
183
105
  }
184
106
 
@@ -194,67 +116,4 @@ export function addExtraPreprocessors(options: ResolvedOptions, config: Resolved
194
116
  options.preprocess = [...prependPreprocessors, options.preprocess, ...appendPreprocessors];
195
117
  }
196
118
  }
197
- const generateMissingSourceMaps = !!options.experimental?.generateMissingPreprocessorSourcemaps;
198
- if (options.preprocess && generateMissingSourceMaps) {
199
- options.preprocess = Array.isArray(options.preprocess)
200
- ? options.preprocess.map((p, i) => validateSourceMapOutputWrapper(p, i))
201
- : validateSourceMapOutputWrapper(options.preprocess, 0);
202
- }
203
- }
204
-
205
- function validateSourceMapOutputWrapper(group: PreprocessorGroup, i: number): PreprocessorGroup {
206
- const wrapper: PreprocessorGroup = {};
207
-
208
- for (const [processorType, processorFn] of Object.entries(group) as Array<
209
- // eslint-disable-next-line no-unused-vars
210
- [keyof PreprocessorGroup, (options: { filename?: string; content: string }) => Processed]
211
- >) {
212
- wrapper[processorType] = async (options) => {
213
- const result = await processorFn(options);
214
-
215
- if (result && result.code !== options.content) {
216
- let invalidMap = false;
217
- if (!result.map) {
218
- invalidMap = true;
219
- log.warn.enabled &&
220
- log.warn.once(
221
- `preprocessor at index ${i} did not return a sourcemap for ${processorType} transform`,
222
- {
223
- filename: options.filename,
224
- type: processorType,
225
- processor: processorFn.toString()
226
- }
227
- );
228
- } else if ((result.map as any)?.mappings === '') {
229
- invalidMap = true;
230
- log.warn.enabled &&
231
- log.warn.once(
232
- `preprocessor at index ${i} returned an invalid empty sourcemap for ${processorType} transform`,
233
- {
234
- filename: options.filename,
235
- type: processorType,
236
- processor: processorFn.toString()
237
- }
238
- );
239
- }
240
- if (invalidMap) {
241
- try {
242
- const map = await buildSourceMap(options.content, result.code, options.filename);
243
- if (map) {
244
- log.debug.enabled &&
245
- log.debug(
246
- `adding generated sourcemap to preprocesor result for ${options.filename}`
247
- );
248
- result.map = map;
249
- }
250
- } catch (e) {
251
- log.error(`failed to build sourcemap`, e);
252
- }
253
- }
254
- }
255
- return result;
256
- };
257
- }
258
-
259
- return wrapper;
260
119
  }