@rsvelte/vite-plugin-svelte 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +28 -0
- package/package.json +59 -0
- package/src/index.js +42 -0
- package/src/plugins/compile-module.js +130 -0
- package/src/plugins/compile.js +65 -0
- package/src/plugins/configure.js +97 -0
- package/src/plugins/hot-update.js +196 -0
- package/src/plugins/load-compiled-css.js +66 -0
- package/src/plugins/load-custom.js +42 -0
- package/src/plugins/preprocess.js +208 -0
- package/src/plugins/setup-optimizer.js +340 -0
- package/src/preprocess.js +166 -0
- package/src/public.d.ts +217 -0
- package/src/types/compile.d.ts +37 -0
- package/src/types/id.d.ts +46 -0
- package/src/types/log.d.ts +24 -0
- package/src/types/options.d.ts +21 -0
- package/src/types/plugin-api.d.ts +15 -0
- package/src/types/vite-plugin-svelte-stats.d.ts +30 -0
- package/src/utils/compile.js +152 -0
- package/src/utils/constants.js +36 -0
- package/src/utils/dependencies.js +60 -0
- package/src/utils/error.js +167 -0
- package/src/utils/hash.js +43 -0
- package/src/utils/id.js +274 -0
- package/src/utils/load-svelte-config.js +80 -0
- package/src/utils/log.js +256 -0
- package/src/utils/options.js +618 -0
- package/src/utils/preprocess.js +89 -0
- package/src/utils/sourcemaps.js +82 -0
- package/src/utils/svelte-version.js +46 -0
- package/src/utils/vite-plugin-svelte-stats.js +248 -0
- package/src/utils/watch.js +95 -0
- package/types/index.d.ts +228 -0
- package/types/index.d.ts.map +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 [these people](https://github.com/sveltejs/vite-plugin-svelte/graphs/contributors)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @sveltejs/vite-plugin-svelte
|
|
2
|
+
|
|
3
|
+
The official [Svelte](https://svelte.dev) plugin for [Vite](https://vitejs.dev).
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```js
|
|
8
|
+
// vite.config.js
|
|
9
|
+
import { defineConfig } from 'vite';
|
|
10
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
11
|
+
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
plugins: [
|
|
14
|
+
svelte({
|
|
15
|
+
/* plugin options */
|
|
16
|
+
})
|
|
17
|
+
]
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Documentation
|
|
22
|
+
|
|
23
|
+
- [Plugin options](../../docs/config.md)
|
|
24
|
+
- [FAQ](../../docs/faq.md)
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
[MIT](./LICENSE)
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rsvelte/vite-plugin-svelte",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "dominikg",
|
|
6
|
+
"files": [
|
|
7
|
+
"src",
|
|
8
|
+
"types"
|
|
9
|
+
],
|
|
10
|
+
"type": "module",
|
|
11
|
+
"types": "types/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./types/index.d.ts",
|
|
15
|
+
"default": "./src/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"check:publint": "publint --strict",
|
|
20
|
+
"check:types": "tsc --noEmit",
|
|
21
|
+
"generate:types": "dts-buddy -m \"@rsvelte/vite-plugin-svelte:src/public.d.ts\""
|
|
22
|
+
},
|
|
23
|
+
"engines": {
|
|
24
|
+
"node": "^20.19 || ^22.12 || >=24"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/baseballyama/vite-plugin-svelte.git",
|
|
29
|
+
"directory": "packages/vite-plugin-svelte"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"vite-plugin",
|
|
33
|
+
"vite plugin",
|
|
34
|
+
"vite",
|
|
35
|
+
"svelte"
|
|
36
|
+
],
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/baseballyama/vite-plugin-svelte/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/baseballyama/vite-plugin-svelte#readme",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@rsvelte/compiler": ">=0.1.0",
|
|
43
|
+
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
|
|
44
|
+
"deepmerge": "^4.3.1",
|
|
45
|
+
"magic-string": "^0.30.21",
|
|
46
|
+
"obug": "^2.1.0",
|
|
47
|
+
"vitefu": "^1.1.1"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"svelte": "^5.0.0",
|
|
51
|
+
"vite": "^6.3.0 || ^7.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/debug": "^4.1.12",
|
|
55
|
+
"sass": "^1.94.2",
|
|
56
|
+
"svelte": "^5.45.4",
|
|
57
|
+
"vite": "^7.2.6"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { log } from './utils/log.js';
|
|
3
|
+
import { configure } from './plugins/configure.js';
|
|
4
|
+
import { preprocess } from './plugins/preprocess.js';
|
|
5
|
+
import { compile } from './plugins/compile.js';
|
|
6
|
+
import { loadCompiledCss } from './plugins/load-compiled-css.js';
|
|
7
|
+
import { setupOptimizer } from './plugins/setup-optimizer.js';
|
|
8
|
+
import { compileModule } from './plugins/compile-module.js';
|
|
9
|
+
import { svelteInspector } from '@sveltejs/vite-plugin-svelte-inspector';
|
|
10
|
+
import { loadCustom } from './plugins/load-custom.js';
|
|
11
|
+
import { hotUpdate } from './plugins/hot-update.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* returns a list of plugins to handle svelte files
|
|
15
|
+
* plugins are named `vite-plugin-svelte:<task>`
|
|
16
|
+
*
|
|
17
|
+
* @param {Partial<import('./public.d.ts').Options>} [inlineOptions]
|
|
18
|
+
* @returns {import('vite').Plugin[]}
|
|
19
|
+
*/
|
|
20
|
+
export function svelte(inlineOptions) {
|
|
21
|
+
if (process.env.DEBUG != null) {
|
|
22
|
+
log.setLevel('debug');
|
|
23
|
+
}
|
|
24
|
+
/** @type {import('./types/plugin-api.js').PluginAPI} */
|
|
25
|
+
// @ts-expect-error initialize empty to guard against early use
|
|
26
|
+
const api = {}; // initialized by configure plugin, used in others
|
|
27
|
+
return [
|
|
28
|
+
{ name: 'vite-plugin-svelte' }, // marker for detection logic in other plugins that expect this name
|
|
29
|
+
configure(api, inlineOptions),
|
|
30
|
+
setupOptimizer(api),
|
|
31
|
+
loadCompiledCss(api),
|
|
32
|
+
loadCustom(api),
|
|
33
|
+
preprocess(api),
|
|
34
|
+
compile(api),
|
|
35
|
+
compileModule(api),
|
|
36
|
+
hotUpdate(api),
|
|
37
|
+
svelteInspector()
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { vitePreprocess } from './preprocess.js';
|
|
42
|
+
export { loadSvelteConfig } from './utils/load-svelte-config.js';
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { buildModuleIdFilter, buildModuleIdParser } from '../utils/id.js';
|
|
2
|
+
import * as svelteCompiler from '@rsvelte/compiler';
|
|
3
|
+
import { log, logCompilerWarnings } from '../utils/log.js';
|
|
4
|
+
import { toRollupError } from '../utils/error.js';
|
|
5
|
+
import { isSvelteWithAsync } from '../utils/svelte-version.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @param {import('../types/plugin-api.d.ts').PluginAPI} api
|
|
9
|
+
* @returns {import('vite').Plugin}
|
|
10
|
+
*/
|
|
11
|
+
export function compileModule(api) {
|
|
12
|
+
/**
|
|
13
|
+
* @type {import("../types/options.js").ResolvedOptions}
|
|
14
|
+
*/
|
|
15
|
+
let options;
|
|
16
|
+
/**
|
|
17
|
+
* @type {import("../types/id.js").ModuleIdParser}
|
|
18
|
+
*/
|
|
19
|
+
let idParser;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @type {import('@rsvelte/compiler').ModuleCompileOptions}
|
|
23
|
+
*/
|
|
24
|
+
let staticModuleCompileOptions;
|
|
25
|
+
|
|
26
|
+
/** @type {import('vite').Plugin} */
|
|
27
|
+
const plugin = {
|
|
28
|
+
name: 'vite-plugin-svelte:compile-module',
|
|
29
|
+
enforce: 'post',
|
|
30
|
+
async configResolved() {
|
|
31
|
+
options = api.options;
|
|
32
|
+
//@ts-expect-error transform defined below but filter not in type
|
|
33
|
+
plugin.transform.filter = buildModuleIdFilter(options);
|
|
34
|
+
idParser = buildModuleIdParser(options);
|
|
35
|
+
staticModuleCompileOptions = filterNonModuleCompilerOptions(options.compilerOptions);
|
|
36
|
+
},
|
|
37
|
+
transform: {
|
|
38
|
+
async handler(code, id) {
|
|
39
|
+
const ssr = this.environment.config.consumer === 'server';
|
|
40
|
+
const moduleRequest = idParser(id, ssr);
|
|
41
|
+
if (!moduleRequest) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const filename = moduleRequest.filename;
|
|
45
|
+
/** @type {import('@rsvelte/compiler').CompileOptions} */
|
|
46
|
+
const compileOptions = {
|
|
47
|
+
...staticModuleCompileOptions,
|
|
48
|
+
dev: !this.environment.config.isProduction,
|
|
49
|
+
generate: ssr ? 'server' : 'client',
|
|
50
|
+
filename
|
|
51
|
+
};
|
|
52
|
+
const dynamicCompileOptions = await options?.dynamicCompileOptions?.({
|
|
53
|
+
filename,
|
|
54
|
+
code,
|
|
55
|
+
compileOptions
|
|
56
|
+
});
|
|
57
|
+
if (dynamicCompileOptions && log.debug.enabled) {
|
|
58
|
+
log.debug(
|
|
59
|
+
`dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`,
|
|
60
|
+
undefined,
|
|
61
|
+
'compileModule'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const finalCompileOptions = dynamicCompileOptions
|
|
65
|
+
? {
|
|
66
|
+
...compileOptions,
|
|
67
|
+
...dynamicCompileOptions
|
|
68
|
+
}
|
|
69
|
+
: compileOptions;
|
|
70
|
+
if (dynamicCompileOptions?.experimental) {
|
|
71
|
+
finalCompileOptions.experimental = {
|
|
72
|
+
...compileOptions.experimental,
|
|
73
|
+
...dynamicCompileOptions.experimental
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const finalModuleCompileOptions = filterNonModuleCompilerOptions(finalCompileOptions);
|
|
77
|
+
if (log.debug.enabled) {
|
|
78
|
+
log.debug(
|
|
79
|
+
`final ModuleCompileOptions for ${filename}: ${JSON.stringify(finalModuleCompileOptions)}`,
|
|
80
|
+
undefined,
|
|
81
|
+
'compileModule'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
const compileResult = svelteCompiler.compileModule(code, finalModuleCompileOptions);
|
|
86
|
+
logCompilerWarnings(moduleRequest, compileResult.warnings, options);
|
|
87
|
+
return compileResult.js;
|
|
88
|
+
} catch (e) {
|
|
89
|
+
throw toRollupError(e, options);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
return plugin;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
*
|
|
99
|
+
* @param {import('@rsvelte/compiler').CompileOptions} compilerOptions
|
|
100
|
+
* @return {import('@rsvelte/compiler').ModuleCompileOptions}
|
|
101
|
+
*/
|
|
102
|
+
function filterNonModuleCompilerOptions(compilerOptions) {
|
|
103
|
+
/** @type {Array<keyof import('@rsvelte/compiler').ModuleCompileOptions>} */
|
|
104
|
+
const knownModuleCompileOptionNames = ['dev', 'generate', 'filename', 'rootDir', 'warningFilter'];
|
|
105
|
+
if (isSvelteWithAsync) {
|
|
106
|
+
knownModuleCompileOptionNames.push('experimental');
|
|
107
|
+
}
|
|
108
|
+
// not typed but this is temporary until svelte itself ignores CompileOptions passed to compileModule
|
|
109
|
+
const experimentalModuleCompilerOptionNames = ['async'];
|
|
110
|
+
|
|
111
|
+
/** @type {import('@rsvelte/compiler').ModuleCompileOptions} */
|
|
112
|
+
const filtered = filterByPropNames(compilerOptions, knownModuleCompileOptionNames);
|
|
113
|
+
if (filtered.experimental) {
|
|
114
|
+
filtered.experimental = filterByPropNames(
|
|
115
|
+
filtered.experimental,
|
|
116
|
+
experimentalModuleCompilerOptionNames
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
return filtered;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
*
|
|
124
|
+
* @param {object} o
|
|
125
|
+
* @param {string[]} names
|
|
126
|
+
* @returns {object}
|
|
127
|
+
*/
|
|
128
|
+
function filterByPropNames(o, names) {
|
|
129
|
+
return Object.fromEntries(Object.entries(o).filter(([name]) => names.includes(name)));
|
|
130
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { toRollupError } from '../utils/error.js';
|
|
2
|
+
import { logCompilerWarnings } from '../utils/log.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {import('../types/plugin-api.d.ts').PluginAPI} api
|
|
6
|
+
* @returns {import('vite').Plugin}
|
|
7
|
+
*/
|
|
8
|
+
export function compile(api) {
|
|
9
|
+
/**
|
|
10
|
+
* @type {import("../types/options.js").ResolvedOptions}
|
|
11
|
+
*/
|
|
12
|
+
let options;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @type {import("../types/compile.d.ts").CompileSvelte}
|
|
16
|
+
*/
|
|
17
|
+
let compileSvelte;
|
|
18
|
+
/** @type {import('vite').Plugin} */
|
|
19
|
+
const plugin = {
|
|
20
|
+
name: 'vite-plugin-svelte:compile',
|
|
21
|
+
configResolved() {
|
|
22
|
+
//@ts-expect-error defined below but filter not in type
|
|
23
|
+
plugin.transform.filter = api.filter;
|
|
24
|
+
options = api.options;
|
|
25
|
+
compileSvelte = api.compileSvelte;
|
|
26
|
+
},
|
|
27
|
+
transform: {
|
|
28
|
+
async handler(code, id) {
|
|
29
|
+
const ssr = this.environment.config.consumer === 'server';
|
|
30
|
+
const svelteRequest = api.idParser(id, ssr);
|
|
31
|
+
if (!svelteRequest || svelteRequest.raw) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let compileData;
|
|
35
|
+
try {
|
|
36
|
+
compileData = await compileSvelte(
|
|
37
|
+
svelteRequest,
|
|
38
|
+
code,
|
|
39
|
+
options,
|
|
40
|
+
this.getCombinedSourcemap()
|
|
41
|
+
);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
throw toRollupError(e, options);
|
|
44
|
+
}
|
|
45
|
+
if (compileData.compiled?.warnings) {
|
|
46
|
+
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
...compileData.compiled.js,
|
|
51
|
+
moduleType: 'js',
|
|
52
|
+
meta: {
|
|
53
|
+
vite: {
|
|
54
|
+
lang: compileData.lang
|
|
55
|
+
},
|
|
56
|
+
svelte: {
|
|
57
|
+
css: compileData.compiled.css
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return plugin;
|
|
65
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { isDebugNamespaceEnabled, log } from '../utils/log.js';
|
|
3
|
+
import * as vite from 'vite';
|
|
4
|
+
import { VitePluginSvelteStats } from '../utils/vite-plugin-svelte-stats.js';
|
|
5
|
+
import {
|
|
6
|
+
buildExtraViteConfig,
|
|
7
|
+
validateInlineOptions,
|
|
8
|
+
resolveOptions,
|
|
9
|
+
preResolveOptions,
|
|
10
|
+
ensureConfigEnvironmentMainFields,
|
|
11
|
+
ensureConfigEnvironmentConditions
|
|
12
|
+
} from '../utils/options.js';
|
|
13
|
+
import { buildIdFilter, buildIdParser } from '../utils/id.js';
|
|
14
|
+
import { createCompileSvelte } from '../utils/compile.js';
|
|
15
|
+
|
|
16
|
+
// @ts-ignore rolldownVersion
|
|
17
|
+
const { version: viteVersion, rolldownVersion } = vite;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {Partial<import('../public.d.ts').Options>} [inlineOptions]
|
|
21
|
+
* @param {import('../types/plugin-api.d.ts').PluginAPI} api
|
|
22
|
+
* @returns {import('vite').Plugin}
|
|
23
|
+
*/
|
|
24
|
+
export function configure(api, inlineOptions) {
|
|
25
|
+
if (rolldownVersion) {
|
|
26
|
+
log.warn.once(
|
|
27
|
+
`!!! Support for vite 8 beta in vite-plugin-svelte is experimental (rolldown: ${rolldownVersion}, vite: ${viteVersion}) !!!
|
|
28
|
+
See https://github.com/sveltejs/vite-plugin-svelte/issues/1143 for a list of known issues and to report feedback.`.replace(
|
|
29
|
+
/\t+/g,
|
|
30
|
+
'\t'
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
validateInlineOptions(inlineOptions);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @type {import("../types/options.d.ts").PreResolvedOptions}
|
|
39
|
+
*/
|
|
40
|
+
let preOptions;
|
|
41
|
+
|
|
42
|
+
/** @type {import('vite').Plugin} */
|
|
43
|
+
return {
|
|
44
|
+
name: 'vite-plugin-svelte:config',
|
|
45
|
+
api,
|
|
46
|
+
// make sure it runs first
|
|
47
|
+
enforce: 'pre',
|
|
48
|
+
config: {
|
|
49
|
+
order: 'pre',
|
|
50
|
+
async handler(config, configEnv) {
|
|
51
|
+
// setup logger
|
|
52
|
+
if (process.env.DEBUG) {
|
|
53
|
+
log.setLevel('debug');
|
|
54
|
+
} else if (config.logLevel) {
|
|
55
|
+
log.setLevel(config.logLevel);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
preOptions = await preResolveOptions(inlineOptions, config, configEnv);
|
|
59
|
+
// extra vite config
|
|
60
|
+
const extraViteConfig = await buildExtraViteConfig(preOptions, config);
|
|
61
|
+
log.debug('additional vite config', extraViteConfig, 'config');
|
|
62
|
+
return extraViteConfig;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
configResolved: {
|
|
66
|
+
order: 'pre',
|
|
67
|
+
handler(config) {
|
|
68
|
+
const options = resolveOptions(preOptions, config);
|
|
69
|
+
api.options = options;
|
|
70
|
+
if (isDebugNamespaceEnabled('stats')) {
|
|
71
|
+
api.options.stats = new VitePluginSvelteStats();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
api.filter = buildIdFilter(options);
|
|
75
|
+
api.idFilter = api.filter;
|
|
76
|
+
api.idParser = buildIdParser(options);
|
|
77
|
+
api.compileSvelte = createCompileSvelte();
|
|
78
|
+
log.debug('resolved options', api.options, 'config');
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
configEnvironment(name, config, opts) {
|
|
83
|
+
ensureConfigEnvironmentMainFields(name, config, opts);
|
|
84
|
+
// @ts-expect-error the function above should make `resolve.mainFields` non-nullable
|
|
85
|
+
config.resolve.mainFields.unshift('svelte');
|
|
86
|
+
|
|
87
|
+
ensureConfigEnvironmentConditions(name, config, opts);
|
|
88
|
+
// @ts-expect-error the function above should make `resolve.conditions` non-nullable
|
|
89
|
+
config.resolve.conditions.push('svelte');
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
configureServer(server) {
|
|
93
|
+
const { options } = api;
|
|
94
|
+
options.server = server;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { log } from '../utils/log.js';
|
|
2
|
+
import { setupWatchers } from '../utils/watch.js';
|
|
3
|
+
import { SVELTE_VIRTUAL_STYLE_ID_REGEX } from '../utils/constants.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('../types/plugin-api.d.ts').PluginAPI} api
|
|
7
|
+
* @returns {import('vite').Plugin}
|
|
8
|
+
*/
|
|
9
|
+
export function hotUpdate(api) {
|
|
10
|
+
/**
|
|
11
|
+
* @type {import("../types/options.js").ResolvedOptions}
|
|
12
|
+
*/
|
|
13
|
+
let options;
|
|
14
|
+
/**
|
|
15
|
+
* @type {import('../types/id.d.ts').IdParser}
|
|
16
|
+
*/
|
|
17
|
+
let idParser;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @type {Map<string|null,string>}
|
|
22
|
+
*/
|
|
23
|
+
const transformResultCache = new Map();
|
|
24
|
+
|
|
25
|
+
/** @type {import('vite').Plugin} */
|
|
26
|
+
const plugin = {
|
|
27
|
+
name: 'vite-plugin-svelte:hot-update',
|
|
28
|
+
enforce: 'post',
|
|
29
|
+
configResolved() {
|
|
30
|
+
options = api.options;
|
|
31
|
+
idParser = api.idParser;
|
|
32
|
+
|
|
33
|
+
// @ts-expect-error
|
|
34
|
+
plugin.transform.filter = {
|
|
35
|
+
id: {
|
|
36
|
+
// reinclude virtual styles to get their output
|
|
37
|
+
include: [...api.filter.id.include, SVELTE_VIRTUAL_STYLE_ID_REGEX],
|
|
38
|
+
exclude: [
|
|
39
|
+
// ignore files in node_modules, we don't hot update them
|
|
40
|
+
/\/node_modules\//,
|
|
41
|
+
// remove style exclusion
|
|
42
|
+
...api.filter.id.exclude.filter((filter) => filter !== SVELTE_VIRTUAL_STYLE_ID_REGEX)
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
applyToEnvironment(env) {
|
|
49
|
+
// we only handle updates for client components
|
|
50
|
+
// ssr frameworks have to handle updating/reloading themselves as v-p-s can't know what they prefer
|
|
51
|
+
const hmrEnabled = options.compilerOptions.hmr && options.emitCss;
|
|
52
|
+
return hmrEnabled && env.config.consumer === 'client';
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
configureServer(server) {
|
|
56
|
+
const clientEnvironment = Object.values(server.environments).find(
|
|
57
|
+
(e) => e.config.consumer === 'client'
|
|
58
|
+
);
|
|
59
|
+
if (clientEnvironment) {
|
|
60
|
+
setupWatchers(options);
|
|
61
|
+
} else {
|
|
62
|
+
log.warn(
|
|
63
|
+
'No client environment found, not adding watchers for svelte config and preprocessor dependencies'
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
buildStart() {
|
|
69
|
+
transformResultCache.clear();
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
transform: {
|
|
73
|
+
order: 'post',
|
|
74
|
+
handler(code, id) {
|
|
75
|
+
transformResultCache.set(id, code);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
hotUpdate: {
|
|
79
|
+
order: 'post',
|
|
80
|
+
async handler(ctx) {
|
|
81
|
+
const svelteRequest = idParser(ctx.file, false, ctx.timestamp);
|
|
82
|
+
if (svelteRequest) {
|
|
83
|
+
const { modules } = ctx;
|
|
84
|
+
const svelteModules = [];
|
|
85
|
+
const nonSvelteModules = [];
|
|
86
|
+
for (const mod of modules) {
|
|
87
|
+
if (transformResultCache.has(mod.id)) {
|
|
88
|
+
svelteModules.push(mod);
|
|
89
|
+
} else {
|
|
90
|
+
nonSvelteModules.push(mod);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (svelteModules.length === 0) {
|
|
95
|
+
return; // nothing to do for us
|
|
96
|
+
}
|
|
97
|
+
const affectedModules = [];
|
|
98
|
+
const prevResults = svelteModules.map((m) => transformResultCache.get(m.id));
|
|
99
|
+
|
|
100
|
+
/** @type {Set<string>} */
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
/** @param {string} url */
|
|
103
|
+
const transformRequest = async (url) => {
|
|
104
|
+
if (!seen.has(url)) {
|
|
105
|
+
seen.add(url);
|
|
106
|
+
await this.environment.transformRequest(url);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// Transform the Svelte component itself first, so that the
|
|
111
|
+
// CSS cache always gets updated.
|
|
112
|
+
await transformRequest(svelteRequest.filename);
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < svelteModules.length; i++) {
|
|
115
|
+
const mod = svelteModules[i];
|
|
116
|
+
const prev = prevResults[i];
|
|
117
|
+
await transformRequest(mod.url);
|
|
118
|
+
const next = transformResultCache.get(mod.id);
|
|
119
|
+
if (hasCodeChanged(prev, next, mod.id)) {
|
|
120
|
+
affectedModules.push(mod);
|
|
121
|
+
} else {
|
|
122
|
+
log.debug(
|
|
123
|
+
`skipping hot update for ${mod.id} because result is unchanged`,
|
|
124
|
+
undefined,
|
|
125
|
+
'hmr'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
log.debug(
|
|
130
|
+
`hotUpdate for ${svelteRequest.id} result: [${affectedModules.map((m) => m.id).join(', ')}]`,
|
|
131
|
+
undefined,
|
|
132
|
+
'hmr'
|
|
133
|
+
);
|
|
134
|
+
return [...affectedModules, ...nonSvelteModules];
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return plugin;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* @param {string | undefined | null} prev
|
|
145
|
+
* @param {string | undefined | null} next
|
|
146
|
+
* @param {string | null} id
|
|
147
|
+
* @returns {boolean}
|
|
148
|
+
*/
|
|
149
|
+
function hasCodeChanged(prev, next, id) {
|
|
150
|
+
const isStrictEqual = nullSafeEqual(prev, next);
|
|
151
|
+
if (isStrictEqual) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const isLooseEqual = nullSafeEqual(normalize(prev), normalize(next));
|
|
155
|
+
if (!isStrictEqual && isLooseEqual) {
|
|
156
|
+
log.debug(
|
|
157
|
+
`ignoring compiler output change for ${id} as it is equal to previous output after normalization`,
|
|
158
|
+
undefined,
|
|
159
|
+
'hmr'
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
return !isLooseEqual;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param {string | null | undefined} prev
|
|
167
|
+
* @param {string | null | undefined} next
|
|
168
|
+
* @returns {boolean}
|
|
169
|
+
*/
|
|
170
|
+
function nullSafeEqual(prev, next) {
|
|
171
|
+
return (prev == null && next == null) || (prev != null && next != null && prev === next);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* remove code that only changes metadata and does not require a js update for the component to keep working
|
|
176
|
+
*
|
|
177
|
+
* 1) location numbers argument from $.add_locations calls in svelte output eg [[1,2],[3,4]]
|
|
178
|
+
* 2) timestamp queries added to imports by vite eg ?t=0123456789123
|
|
179
|
+
*
|
|
180
|
+
* @param {string | null | undefined } code
|
|
181
|
+
* @returns {string | null | undefined}
|
|
182
|
+
*/
|
|
183
|
+
function normalize(code) {
|
|
184
|
+
if (code == null) {
|
|
185
|
+
return code;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return (
|
|
189
|
+
code
|
|
190
|
+
// svelte5 $.add_locations line numbers argument [[1,2],[3,4]]
|
|
191
|
+
// uses matching group replace to keep the template argument intact
|
|
192
|
+
.replace(/(\$\.add_locations\(.*), (\[\[[\d, [\]]+]])\)/g, '$1, []')
|
|
193
|
+
// vite import analysis timestamp queries, ?t=0123456789123&
|
|
194
|
+
.replace(/[?&]t=\d{13}\b/g, '')
|
|
195
|
+
);
|
|
196
|
+
}
|