@sveltejs/vite-plugin-svelte 1.0.0-next.8 → 1.0.1
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/README.md +16 -50
- package/dist/index.cjs +1887 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +231 -214
- package/dist/index.js +1392 -388
- package/dist/index.js.map +1 -0
- package/package.json +49 -33
- package/src/handle-hot-update.ts +117 -0
- package/src/index.ts +242 -0
- package/src/ui/inspector/Inspector.svelte +245 -0
- package/src/ui/inspector/load-inspector.js +15 -0
- package/src/ui/inspector/plugin.ts +106 -0
- package/src/utils/__tests__/dependencies.spec.ts +43 -0
- package/src/utils/__tests__/sourcemap.spec.ts +25 -0
- package/src/utils/compile.ts +159 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/dependencies.ts +241 -0
- package/src/utils/error.ts +95 -0
- package/src/utils/esbuild.ts +84 -0
- package/src/utils/hash.ts +32 -0
- package/src/utils/id.ts +135 -0
- package/src/utils/load-svelte-config.ts +115 -0
- package/src/utils/log.ts +211 -0
- package/src/utils/optimizer.ts +45 -0
- package/src/utils/options.ts +707 -0
- package/src/utils/preprocess.ts +252 -0
- package/src/utils/resolve.ts +57 -0
- package/src/utils/sourcemap.ts +58 -0
- package/src/utils/vite-plugin-svelte-cache.ts +127 -0
- package/src/utils/watch.ts +110 -0
- package/CHANGELOG.md +0 -38
package/src/index.ts
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { HmrContext, ModuleNode, Plugin, ResolvedConfig, UserConfig } from 'vite';
|
|
3
|
+
import { handleHotUpdate } from './handle-hot-update';
|
|
4
|
+
import { log, logCompilerWarnings } from './utils/log';
|
|
5
|
+
import { CompileData, createCompileSvelte } from './utils/compile';
|
|
6
|
+
import { buildIdParser, IdParser, SvelteRequest } from './utils/id';
|
|
7
|
+
import {
|
|
8
|
+
buildExtraViteConfig,
|
|
9
|
+
validateInlineOptions,
|
|
10
|
+
Options,
|
|
11
|
+
ResolvedOptions,
|
|
12
|
+
resolveOptions,
|
|
13
|
+
patchResolvedViteConfig,
|
|
14
|
+
preResolveOptions
|
|
15
|
+
} from './utils/options';
|
|
16
|
+
import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache';
|
|
17
|
+
|
|
18
|
+
import { ensureWatchedFile, setupWatchers } from './utils/watch';
|
|
19
|
+
import { resolveViaPackageJsonSvelte } from './utils/resolve';
|
|
20
|
+
import { PartialResolvedId } from 'rollup';
|
|
21
|
+
import { toRollupError } from './utils/error';
|
|
22
|
+
import { saveSvelteMetadata } from './utils/optimizer';
|
|
23
|
+
import { svelteInspector } from './ui/inspector/plugin';
|
|
24
|
+
|
|
25
|
+
interface PluginAPI {
|
|
26
|
+
/**
|
|
27
|
+
* must not be modified, should not be used outside of vite-plugin-svelte repo
|
|
28
|
+
* @internal
|
|
29
|
+
* @experimental
|
|
30
|
+
*/
|
|
31
|
+
options?: ResolvedOptions;
|
|
32
|
+
// TODO expose compile cache here so other utility plugins can use it
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
|
|
36
|
+
if (process.env.DEBUG != null) {
|
|
37
|
+
log.setLevel('debug');
|
|
38
|
+
}
|
|
39
|
+
validateInlineOptions(inlineOptions);
|
|
40
|
+
const cache = new VitePluginSvelteCache();
|
|
41
|
+
// updated in configResolved hook
|
|
42
|
+
let requestParser: IdParser;
|
|
43
|
+
let options: ResolvedOptions;
|
|
44
|
+
let viteConfig: ResolvedConfig;
|
|
45
|
+
/* eslint-disable no-unused-vars */
|
|
46
|
+
let compileSvelte: (
|
|
47
|
+
svelteRequest: SvelteRequest,
|
|
48
|
+
code: string,
|
|
49
|
+
options: Partial<ResolvedOptions>
|
|
50
|
+
) => Promise<CompileData>;
|
|
51
|
+
/* eslint-enable no-unused-vars */
|
|
52
|
+
|
|
53
|
+
let resolvedSvelteSSR: Promise<PartialResolvedId | null>;
|
|
54
|
+
const api: PluginAPI = {};
|
|
55
|
+
const plugins: Plugin[] = [
|
|
56
|
+
{
|
|
57
|
+
name: 'vite-plugin-svelte',
|
|
58
|
+
// make sure our resolver runs before vite internal resolver to resolve svelte field correctly
|
|
59
|
+
enforce: 'pre',
|
|
60
|
+
api,
|
|
61
|
+
async config(config, configEnv): Promise<Partial<UserConfig>> {
|
|
62
|
+
// setup logger
|
|
63
|
+
if (process.env.DEBUG) {
|
|
64
|
+
log.setLevel('debug');
|
|
65
|
+
} else if (config.logLevel) {
|
|
66
|
+
log.setLevel(config.logLevel);
|
|
67
|
+
}
|
|
68
|
+
// @ts-expect-error temporarily lend the options variable until fixed in configResolved
|
|
69
|
+
options = await preResolveOptions(inlineOptions, config, configEnv);
|
|
70
|
+
// extra vite config
|
|
71
|
+
const extraViteConfig = buildExtraViteConfig(options, config);
|
|
72
|
+
log.debug('additional vite config', extraViteConfig);
|
|
73
|
+
return extraViteConfig;
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
async configResolved(config) {
|
|
77
|
+
options = resolveOptions(options, config);
|
|
78
|
+
patchResolvedViteConfig(config, options);
|
|
79
|
+
requestParser = buildIdParser(options);
|
|
80
|
+
compileSvelte = createCompileSvelte(options);
|
|
81
|
+
viteConfig = config;
|
|
82
|
+
// TODO deep clone to avoid mutability from outside?
|
|
83
|
+
api.options = options;
|
|
84
|
+
log.debug('resolved options', options);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
async buildStart() {
|
|
88
|
+
if (!options.experimental?.prebundleSvelteLibraries) return;
|
|
89
|
+
const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options);
|
|
90
|
+
if (isSvelteMetadataChanged) {
|
|
91
|
+
// Force Vite to optimize again. Although we mutate the config here, it works because
|
|
92
|
+
// Vite's optimizer runs after `buildStart()`.
|
|
93
|
+
// TODO: verify this works in vite3
|
|
94
|
+
viteConfig.optimizeDeps.force = true;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
configureServer(server) {
|
|
99
|
+
// eslint-disable-next-line no-unused-vars
|
|
100
|
+
options.server = server;
|
|
101
|
+
setupWatchers(options, cache, requestParser);
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
load(id, opts) {
|
|
105
|
+
const ssr = !!opts?.ssr;
|
|
106
|
+
const svelteRequest = requestParser(id, !!ssr);
|
|
107
|
+
if (svelteRequest) {
|
|
108
|
+
const { filename, query } = svelteRequest;
|
|
109
|
+
// virtual css module
|
|
110
|
+
if (query.svelte && query.type === 'style') {
|
|
111
|
+
const css = cache.getCSS(svelteRequest);
|
|
112
|
+
if (css) {
|
|
113
|
+
log.debug(`load returns css for ${filename}`);
|
|
114
|
+
return css;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// prevent vite asset plugin from loading files as url that should be compiled in transform
|
|
118
|
+
if (viteConfig.assetsInclude(filename)) {
|
|
119
|
+
log.debug(`load returns raw content for ${filename}`);
|
|
120
|
+
return fs.readFileSync(filename, 'utf-8');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
async resolveId(importee, importer, opts) {
|
|
126
|
+
const ssr = !!opts?.ssr;
|
|
127
|
+
const svelteRequest = requestParser(importee, ssr);
|
|
128
|
+
if (svelteRequest?.query.svelte) {
|
|
129
|
+
if (svelteRequest.query.type === 'style') {
|
|
130
|
+
// return cssId with root prefix so postcss pipeline of vite finds the directory correctly
|
|
131
|
+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/14
|
|
132
|
+
log.debug(`resolveId resolved virtual css module ${svelteRequest.cssId}`);
|
|
133
|
+
return svelteRequest.cssId;
|
|
134
|
+
}
|
|
135
|
+
log.debug(`resolveId resolved ${importee}`);
|
|
136
|
+
return importee; // query with svelte tag, an id we generated, no need for further analysis
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (ssr && importee === 'svelte') {
|
|
140
|
+
if (!resolvedSvelteSSR) {
|
|
141
|
+
resolvedSvelteSSR = this.resolve('svelte/ssr', undefined, { skipSelf: true }).then(
|
|
142
|
+
(svelteSSR) => {
|
|
143
|
+
log.debug('resolved svelte to svelte/ssr');
|
|
144
|
+
return svelteSSR;
|
|
145
|
+
},
|
|
146
|
+
(err) => {
|
|
147
|
+
log.debug(
|
|
148
|
+
'failed to resolve svelte to svelte/ssr. Update svelte to a version that exports it',
|
|
149
|
+
err
|
|
150
|
+
);
|
|
151
|
+
return null; // returning null here leads to svelte getting resolved regularly
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return resolvedSvelteSSR;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const resolved = resolveViaPackageJsonSvelte(importee, importer, cache);
|
|
159
|
+
if (resolved) {
|
|
160
|
+
log.debug(
|
|
161
|
+
`resolveId resolved ${resolved} via package.json svelte field of ${importee}`
|
|
162
|
+
);
|
|
163
|
+
return resolved;
|
|
164
|
+
}
|
|
165
|
+
} catch (e) {
|
|
166
|
+
log.debug.once(
|
|
167
|
+
`error trying to resolve ${importee} from ${importer} via package.json svelte field `,
|
|
168
|
+
e
|
|
169
|
+
);
|
|
170
|
+
// this error most likely happens due to non-svelte related importee/importers so swallow it here
|
|
171
|
+
// in case it really way a svelte library, users will notice anyway. (lib not working due to failed resolve)
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
async transform(code, id, opts) {
|
|
176
|
+
const ssr = !!opts?.ssr;
|
|
177
|
+
const svelteRequest = requestParser(id, ssr);
|
|
178
|
+
if (!svelteRequest || svelteRequest.query.svelte) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
let compileData;
|
|
182
|
+
try {
|
|
183
|
+
compileData = await compileSvelte(svelteRequest, code, options);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
cache.setError(svelteRequest, e);
|
|
186
|
+
throw toRollupError(e, options);
|
|
187
|
+
}
|
|
188
|
+
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
|
|
189
|
+
cache.update(compileData);
|
|
190
|
+
if (compileData.dependencies?.length && options.server) {
|
|
191
|
+
compileData.dependencies.forEach((d) => {
|
|
192
|
+
ensureWatchedFile(options.server!.watcher, d, options.root);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
log.debug(`transform returns compiled js for ${svelteRequest.filename}`);
|
|
196
|
+
return {
|
|
197
|
+
...compileData.compiled.js,
|
|
198
|
+
meta: {
|
|
199
|
+
vite: {
|
|
200
|
+
lang: compileData.lang
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
handleHotUpdate(ctx: HmrContext): void | Promise<Array<ModuleNode> | void> {
|
|
207
|
+
if (!options.hot || !options.emitCss) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const svelteRequest = requestParser(ctx.file, false, ctx.timestamp);
|
|
211
|
+
if (svelteRequest) {
|
|
212
|
+
try {
|
|
213
|
+
return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options);
|
|
214
|
+
} catch (e) {
|
|
215
|
+
throw toRollupError(e, options);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
];
|
|
221
|
+
plugins.push(svelteInspector());
|
|
222
|
+
return plugins.filter(Boolean);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export { loadSvelteConfig } from './utils/load-svelte-config';
|
|
226
|
+
|
|
227
|
+
export {
|
|
228
|
+
Options,
|
|
229
|
+
PluginOptions,
|
|
230
|
+
SvelteOptions,
|
|
231
|
+
Preprocessor,
|
|
232
|
+
PreprocessorGroup,
|
|
233
|
+
CompileOptions,
|
|
234
|
+
CssHashGetter,
|
|
235
|
+
Arrayable,
|
|
236
|
+
MarkupPreprocessor,
|
|
237
|
+
ModuleFormat,
|
|
238
|
+
Processed,
|
|
239
|
+
Warning
|
|
240
|
+
} from './utils/options';
|
|
241
|
+
|
|
242
|
+
export { SvelteWarningsMessage } from './utils/log';
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
// do not use TS here so that this component works in non-ts projects too
|
|
3
|
+
import { onMount } from 'svelte';
|
|
4
|
+
// eslint-disable-next-line node/no-missing-import
|
|
5
|
+
import options from 'virtual:svelte-inspector-options';
|
|
6
|
+
const toggle_combo = options.toggleKeyCombo?.toLowerCase().split('-');
|
|
7
|
+
|
|
8
|
+
let enabled = false;
|
|
9
|
+
|
|
10
|
+
const icon = `data:image/svg+xml;base64,${btoa(
|
|
11
|
+
`
|
|
12
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="25" viewBox="0 0 107 128">
|
|
13
|
+
<title>svelte-inspector-logo</title>
|
|
14
|
+
<path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/>
|
|
15
|
+
<polygon points="0,0 15,40 40,20" stroke="#ff3e00" fill="#ff3e00"></polygon>
|
|
16
|
+
</svg>
|
|
17
|
+
`
|
|
18
|
+
.replace(/[\n\r\t\s]+/g, ' ')
|
|
19
|
+
.trim()
|
|
20
|
+
)}`;
|
|
21
|
+
|
|
22
|
+
// location of code in file
|
|
23
|
+
let file_loc;
|
|
24
|
+
// cursor pos and width for file_loc overlay positioning
|
|
25
|
+
let x, y, w;
|
|
26
|
+
|
|
27
|
+
let active_el;
|
|
28
|
+
let toggle_el;
|
|
29
|
+
|
|
30
|
+
let enabled_ts;
|
|
31
|
+
|
|
32
|
+
$: show_toggle =
|
|
33
|
+
options.showToggleButton === 'always' || (options.showToggleButton === 'active' && enabled);
|
|
34
|
+
|
|
35
|
+
function mousemove(event) {
|
|
36
|
+
x = event.x;
|
|
37
|
+
y = event.y;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function findMetaEl(el) {
|
|
41
|
+
while (el) {
|
|
42
|
+
const file = el.__svelte_meta?.loc?.file;
|
|
43
|
+
if (el !== toggle_el && file && !file.includes('node_modules/')) {
|
|
44
|
+
return el;
|
|
45
|
+
}
|
|
46
|
+
el = el.parentNode;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mouseover(event) {
|
|
51
|
+
const el = findMetaEl(event.target);
|
|
52
|
+
if (options.customStyles && el !== active_el) {
|
|
53
|
+
if (active_el) {
|
|
54
|
+
active_el.classList.remove('svelte-inspector-active-target');
|
|
55
|
+
}
|
|
56
|
+
if (el) {
|
|
57
|
+
el.classList.add('svelte-inspector-active-target');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (el) {
|
|
61
|
+
const { file, line, column } = el.__svelte_meta.loc;
|
|
62
|
+
file_loc = `${file}:${line + 1}:${column + 1}`;
|
|
63
|
+
} else {
|
|
64
|
+
file_loc = null;
|
|
65
|
+
}
|
|
66
|
+
active_el = el;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function click(event) {
|
|
70
|
+
if (file_loc) {
|
|
71
|
+
event.preventDefault();
|
|
72
|
+
event.stopPropagation();
|
|
73
|
+
event.stopImmediatePropagation();
|
|
74
|
+
fetch(`/__open-in-editor?file=${encodeURIComponent(file_loc)}`);
|
|
75
|
+
if (options.holdMode && is_holding()) {
|
|
76
|
+
disable();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function is_key_active(key, event) {
|
|
82
|
+
switch (key) {
|
|
83
|
+
case 'shift':
|
|
84
|
+
case 'control':
|
|
85
|
+
case 'alt':
|
|
86
|
+
case 'meta':
|
|
87
|
+
return event.getModifierState(key.charAt(0).toUpperCase() + key.slice(1));
|
|
88
|
+
default:
|
|
89
|
+
return key === event.key.toLowerCase();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function is_combo(event) {
|
|
94
|
+
return toggle_combo?.every((key) => is_key_active(key, event));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function is_holding() {
|
|
98
|
+
return enabled_ts && Date.now() - enabled_ts > 250;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function keydown(event) {
|
|
102
|
+
if (event.repeat || event.key === undefined) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (is_combo(event)) {
|
|
107
|
+
toggle();
|
|
108
|
+
if (options.holdMode && enabled) {
|
|
109
|
+
enabled_ts = Date.now();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function keyup(event) {
|
|
115
|
+
if (event.repeat) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const k = event.key.toLowerCase();
|
|
119
|
+
if (enabled && is_holding() && toggle_combo.includes(k)) {
|
|
120
|
+
disable();
|
|
121
|
+
} else {
|
|
122
|
+
enabled_ts = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function toggle() {
|
|
127
|
+
enabled ? disable() : enable();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function listeners(body, enabled) {
|
|
131
|
+
const l = enabled ? body.addEventListener : body.removeEventListener;
|
|
132
|
+
l('mousemove', mousemove);
|
|
133
|
+
l('mouseover', mouseover);
|
|
134
|
+
l('click', click, true);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function enable() {
|
|
138
|
+
enabled = true;
|
|
139
|
+
const b = document.body;
|
|
140
|
+
if (options.customStyles) {
|
|
141
|
+
b.classList.add('svelte-inspector-enabled');
|
|
142
|
+
}
|
|
143
|
+
listeners(b, enabled);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function disable() {
|
|
147
|
+
enabled = false;
|
|
148
|
+
enabled_ts = null;
|
|
149
|
+
const b = document.body;
|
|
150
|
+
listeners(b, enabled);
|
|
151
|
+
if (options.customStyles) {
|
|
152
|
+
b.classList.remove('svelte-inspector-enabled');
|
|
153
|
+
active_el?.classList.remove('svelte-inspector-active-target');
|
|
154
|
+
}
|
|
155
|
+
active_el = null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
onMount(() => {
|
|
159
|
+
const s = document.createElement('style');
|
|
160
|
+
s.setAttribute('type', 'text/css');
|
|
161
|
+
s.setAttribute('id', 'svelte-inspector-style');
|
|
162
|
+
s.textContent = `:root { --svelte-inspector-icon: url(${icon})};`;
|
|
163
|
+
document.head.append(s);
|
|
164
|
+
if (toggle_combo) {
|
|
165
|
+
document.body.addEventListener('keydown', keydown);
|
|
166
|
+
if (options.holdMode) {
|
|
167
|
+
document.body.addEventListener('keyup', keyup);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return () => {
|
|
171
|
+
// make sure we get rid of everything
|
|
172
|
+
disable();
|
|
173
|
+
const s = document.head.querySelector('#svelte-inspector-style');
|
|
174
|
+
if (s) {
|
|
175
|
+
document.head.removeChild(s);
|
|
176
|
+
}
|
|
177
|
+
if (toggle_combo) {
|
|
178
|
+
document.body.removeEventListener('keydown', keydown);
|
|
179
|
+
if (options.holdMode) {
|
|
180
|
+
document.body.addEventListener('keyup', keyup);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
});
|
|
185
|
+
</script>
|
|
186
|
+
|
|
187
|
+
{#if show_toggle}
|
|
188
|
+
<div
|
|
189
|
+
class="svelte-inspector-toggle"
|
|
190
|
+
class:enabled
|
|
191
|
+
style={`background-image: var(--svelte-inspector-icon);${options.toggleButtonPos
|
|
192
|
+
.split('-')
|
|
193
|
+
.map((p) => `${p}: 8px;`)
|
|
194
|
+
.join('')}`}
|
|
195
|
+
on:click={() => toggle()}
|
|
196
|
+
bind:this={toggle_el}
|
|
197
|
+
/>
|
|
198
|
+
{/if}
|
|
199
|
+
{#if enabled && file_loc}
|
|
200
|
+
<div
|
|
201
|
+
class="svelte-inspector-overlay"
|
|
202
|
+
style:left="{Math.min(x + 3, document.body.clientWidth - w - 10)}px"
|
|
203
|
+
style:top="{y + 30}px"
|
|
204
|
+
bind:offsetWidth={w}
|
|
205
|
+
>
|
|
206
|
+
{file_loc}
|
|
207
|
+
</div>
|
|
208
|
+
{/if}
|
|
209
|
+
|
|
210
|
+
<style>
|
|
211
|
+
:global(body.svelte-inspector-enabled *) {
|
|
212
|
+
cursor: var(--svelte-inspector-icon), crosshair !important;
|
|
213
|
+
}
|
|
214
|
+
:global(.svelte-inspector-active-target) {
|
|
215
|
+
outline: 2px dashed #ff3e00 !important;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.svelte-inspector-overlay {
|
|
219
|
+
position: fixed;
|
|
220
|
+
background-color: rgba(0, 0, 0, 0.8);
|
|
221
|
+
color: #fff;
|
|
222
|
+
padding: 2px 4px;
|
|
223
|
+
border-radius: 5px;
|
|
224
|
+
z-index: 999999;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.svelte-inspector-toggle {
|
|
228
|
+
border: 1px solid #ff3e00;
|
|
229
|
+
border-radius: 8px;
|
|
230
|
+
position: fixed;
|
|
231
|
+
height: 32px;
|
|
232
|
+
width: 32px;
|
|
233
|
+
background-color: white;
|
|
234
|
+
background-position: center;
|
|
235
|
+
background-repeat: no-repeat;
|
|
236
|
+
cursor: pointer;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.svelte-inspector-toggle:not(.enabled) {
|
|
240
|
+
filter: grayscale(1);
|
|
241
|
+
}
|
|
242
|
+
.svelte-inspector-toggle:hover {
|
|
243
|
+
background-color: #facece;
|
|
244
|
+
}
|
|
245
|
+
</style>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// eslint-disable-next-line node/no-missing-import
|
|
2
|
+
import Inspector from 'virtual:svelte-inspector-path:Inspector.svelte';
|
|
3
|
+
|
|
4
|
+
function create_inspector_host() {
|
|
5
|
+
const id = 'svelte-inspector-host';
|
|
6
|
+
if (document.getElementById(id) != null) {
|
|
7
|
+
throw new Error('svelte-inspector-host element already exists');
|
|
8
|
+
}
|
|
9
|
+
const el = document.createElement('div');
|
|
10
|
+
el.setAttribute('id', id);
|
|
11
|
+
document.getElementsByTagName('body')[0].appendChild(el);
|
|
12
|
+
return el;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
new Inspector({ target: create_inspector_host() });
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { Plugin, normalizePath } from 'vite';
|
|
2
|
+
import { log } from '../../utils/log';
|
|
3
|
+
import { InspectorOptions } from '../../utils/options';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
|
|
8
|
+
const defaultInspectorOptions: InspectorOptions = {
|
|
9
|
+
toggleKeyCombo: process.platform === 'win32' ? 'control-shift' : 'meta-shift',
|
|
10
|
+
holdMode: false,
|
|
11
|
+
showToggleButton: 'active',
|
|
12
|
+
toggleButtonPos: 'top-right',
|
|
13
|
+
customStyles: true
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function getInspectorPath() {
|
|
17
|
+
const pluginPath = normalizePath(path.dirname(fileURLToPath(import.meta.url)));
|
|
18
|
+
return pluginPath.replace(/\/vite-plugin-svelte\/dist$/, '/vite-plugin-svelte/src/ui/inspector/');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function svelteInspector(): Plugin {
|
|
22
|
+
const inspectorPath = getInspectorPath();
|
|
23
|
+
log.debug.enabled && log.debug(`svelte inspector path: ${inspectorPath}`);
|
|
24
|
+
let inspectorOptions: InspectorOptions;
|
|
25
|
+
let appendTo: string | undefined;
|
|
26
|
+
let disabled = false;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
name: 'vite-plugin-svelte:inspector',
|
|
30
|
+
apply: 'serve',
|
|
31
|
+
enforce: 'pre',
|
|
32
|
+
|
|
33
|
+
configResolved(config) {
|
|
34
|
+
const vps = config.plugins.find((p) => p.name === 'vite-plugin-svelte');
|
|
35
|
+
if (vps?.api?.options?.experimental?.inspector) {
|
|
36
|
+
inspectorOptions = {
|
|
37
|
+
...defaultInspectorOptions,
|
|
38
|
+
...vps.api.options.experimental.inspector
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (!vps || !inspectorOptions) {
|
|
42
|
+
log.debug('inspector disabled, could not find config');
|
|
43
|
+
disabled = true;
|
|
44
|
+
} else {
|
|
45
|
+
if (vps.api.options.kit && !inspectorOptions.appendTo) {
|
|
46
|
+
const out_dir = path.basename(vps.api.options.kit.outDir || '.svelte-kit');
|
|
47
|
+
inspectorOptions.appendTo = `${out_dir}/runtime/client/start.js`;
|
|
48
|
+
}
|
|
49
|
+
appendTo = inspectorOptions.appendTo;
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
async resolveId(importee: string, importer, options) {
|
|
54
|
+
if (options?.ssr || disabled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (importee.startsWith('virtual:svelte-inspector-options')) {
|
|
58
|
+
return importee;
|
|
59
|
+
} else if (importee.startsWith('virtual:svelte-inspector-path:')) {
|
|
60
|
+
const resolved = importee.replace('virtual:svelte-inspector-path:', inspectorPath);
|
|
61
|
+
log.debug.enabled && log.debug(`resolved ${importee} with ${resolved}`);
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
async load(id, options) {
|
|
67
|
+
if (options?.ssr || disabled) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (id === 'virtual:svelte-inspector-options') {
|
|
71
|
+
return `export default ${JSON.stringify(inspectorOptions ?? {})}`;
|
|
72
|
+
} else if (id.startsWith(inspectorPath)) {
|
|
73
|
+
// read file ourselves to avoid getting shut out by vites fs.allow check
|
|
74
|
+
return await fs.promises.readFile(id, 'utf-8');
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
transform(code: string, id: string, options?: { ssr?: boolean }) {
|
|
79
|
+
if (options?.ssr || disabled || !appendTo) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (id.endsWith(appendTo)) {
|
|
83
|
+
return { code: `${code}\nimport 'virtual:svelte-inspector-path:load-inspector.js'` };
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
transformIndexHtml(html) {
|
|
87
|
+
if (disabled || appendTo) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
html,
|
|
92
|
+
tags: [
|
|
93
|
+
{
|
|
94
|
+
tag: 'script',
|
|
95
|
+
injectTo: 'body',
|
|
96
|
+
attrs: {
|
|
97
|
+
type: 'module',
|
|
98
|
+
// /@id/ is needed, otherwise the virtual: is seen as protocol by browser and cors error happens
|
|
99
|
+
src: '/@id/virtual:svelte-inspector-path:load-inspector.js'
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { findRootSvelteDependencies, needsOptimization } from '../dependencies';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
const __dir = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const e2eTestRoot = path.resolve(__dir, '../../../../../packages/e2e-tests');
|
|
8
|
+
|
|
9
|
+
describe('dependencies', () => {
|
|
10
|
+
describe('findRootSvelteDependencies', () => {
|
|
11
|
+
it('should find svelte dependencies in packages/e2e-test/hmr', () => {
|
|
12
|
+
const deps = findRootSvelteDependencies(path.resolve('packages/e2e-tests/hmr'));
|
|
13
|
+
expect(deps).toHaveLength(1);
|
|
14
|
+
expect(deps[0].name).toBe('e2e-test-dep-svelte-simple');
|
|
15
|
+
expect(deps[0].path).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
it('should find nested svelte dependencies in packages/e2e-test/package-json-svelte-field', () => {
|
|
18
|
+
const deps = findRootSvelteDependencies(path.join(e2eTestRoot, 'package-json-svelte-field'));
|
|
19
|
+
expect(deps).toHaveLength(3);
|
|
20
|
+
const hybrid = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-hybrid');
|
|
21
|
+
expect(hybrid).toBeTruthy();
|
|
22
|
+
expect(hybrid.path).toHaveLength(0);
|
|
23
|
+
const nested = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-nested');
|
|
24
|
+
expect(nested).toBeTruthy();
|
|
25
|
+
expect(nested.path).toHaveLength(0);
|
|
26
|
+
const simple = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-simple');
|
|
27
|
+
expect(simple).toBeTruthy();
|
|
28
|
+
expect(simple.path).toHaveLength(1);
|
|
29
|
+
expect(simple.path[0]).toBe('e2e-test-dep-svelte-nested');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('needsOptimization', () => {
|
|
33
|
+
it('should optimize cjs deps only', () => {
|
|
34
|
+
const testDepsPath = path.join(e2eTestRoot, 'dependencies/package.json');
|
|
35
|
+
const localRequire = createRequire(testDepsPath);
|
|
36
|
+
expect(needsOptimization('e2e-test-dep-cjs-and-esm', localRequire)).toBe(false);
|
|
37
|
+
expect(needsOptimization('e2e-test-dep-cjs-only', localRequire)).toBe(true);
|
|
38
|
+
expect(needsOptimization('e2e-test-dep-esm-only', localRequire)).toBe(false);
|
|
39
|
+
expect(needsOptimization('e2e-test-dep-index-only', localRequire)).toBe(true);
|
|
40
|
+
expect(needsOptimization('e2e-test-dep-types-only', localRequire)).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
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
|
+
});
|