@sveltejs/vite-plugin-svelte 1.3.1 → 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.
- package/dist/index.cjs +456 -346
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +6 -13
- package/dist/index.js +448 -339
- package/dist/index.js.map +1 -1
- package/dist/preprocess.cjs +127 -0
- package/dist/preprocess.cjs.map +1 -0
- package/dist/preprocess.d.ts +9 -0
- package/dist/preprocess.js +96 -0
- package/dist/preprocess.js.map +1 -0
- package/package.json +5 -15
- package/src/handle-hot-update.ts +24 -24
- package/src/index.ts +23 -24
- package/src/preprocess.ts +114 -0
- package/src/utils/compile.ts +71 -23
- package/src/utils/id.ts +55 -3
- package/src/utils/load-raw.ts +132 -0
- package/src/utils/options.ts +16 -9
- package/src/utils/preprocess.ts +9 -150
- package/src/utils/__tests__/sourcemap.spec.ts +0 -25
- package/src/utils/sourcemap.ts +0 -58
package/src/utils/compile.ts
CHANGED
|
@@ -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.
|
|
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,
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/src/utils/options.ts
CHANGED
|
@@ -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
|
*
|
package/src/utils/preprocess.ts
CHANGED
|
@@ -1,93 +1,17 @@
|
|
|
1
|
-
import
|
|
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 {
|
|
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
|
-
}
|
|
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.
|
|
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
|
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { buildMagicString, buildSourceMap } from '../sourcemap';
|
|
3
|
-
|
|
4
|
-
describe('sourcemap', () => {
|
|
5
|
-
describe('buildMagicString', () => {
|
|
6
|
-
it('should return a valid magic string', async () => {
|
|
7
|
-
const from = 'h1{color: blue}\nh2{color: green}\nh3{color: red}\n';
|
|
8
|
-
const to = 'h1{color: blue}\ndiv{color: white}\nh3{color: red}\nh2{color: green}\n';
|
|
9
|
-
const m = await buildMagicString(from, to);
|
|
10
|
-
expect(m).toBeDefined();
|
|
11
|
-
expect(m.original).toBe(from);
|
|
12
|
-
expect(m.toString()).toBe(to);
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
describe('buildSourceMap', () => {
|
|
16
|
-
it('should return a map with mappings and filename', async () => {
|
|
17
|
-
const map = await buildSourceMap('foo', 'bar', 'foo.txt');
|
|
18
|
-
expect(map).toBeDefined();
|
|
19
|
-
expect(map.mappings).toBeDefined();
|
|
20
|
-
expect(map.mappings[0]).toBeDefined();
|
|
21
|
-
expect(map.mappings[0][0]).toBeDefined();
|
|
22
|
-
expect(map.sources[0]).toBe('foo.txt');
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|