@sveltejs/vite-plugin-svelte 1.0.0-next.3 → 1.0.0-next.33

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.
@@ -0,0 +1,247 @@
1
+ import {
2
+ transformWithEsbuild,
3
+ ESBuildOptions,
4
+ ResolvedConfig,
5
+ TransformResult,
6
+ Plugin
7
+ } from 'vite';
8
+ import MagicString from 'magic-string';
9
+ import { preprocess } from 'svelte/compiler';
10
+ import { Preprocessor, PreprocessorGroup, Processed, ResolvedOptions } from './options';
11
+ import { TransformPluginContext } from 'rollup';
12
+ import { log } from './log';
13
+ import { buildSourceMap } from './sourcemap';
14
+
15
+ const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss'];
16
+
17
+ const supportedScriptLangs = ['ts'];
18
+
19
+ function createViteScriptPreprocessor(): Preprocessor {
20
+ return async ({ attributes, content, filename = '' }) => {
21
+ const lang = attributes.lang as string;
22
+ if (!supportedScriptLangs.includes(lang)) return;
23
+ const transformResult = await transformWithEsbuild(content, filename, {
24
+ loader: lang as ESBuildOptions['loader'],
25
+ tsconfigRaw: {
26
+ compilerOptions: {
27
+ // svelte typescript needs this flag to work with type imports
28
+ importsNotUsedAsValues: 'preserve'
29
+ }
30
+ }
31
+ });
32
+ return {
33
+ code: transformResult.code,
34
+ map: transformResult.map
35
+ };
36
+ };
37
+ }
38
+
39
+ function createViteStylePreprocessor(config: ResolvedConfig): Preprocessor {
40
+ const pluginName = 'vite:css';
41
+ const plugin = config.plugins.find((p) => p.name === pluginName);
42
+ if (!plugin) {
43
+ throw new Error(`failed to find plugin ${pluginName}`);
44
+ }
45
+ if (!plugin.transform) {
46
+ throw new Error(`plugin ${pluginName} has no transform`);
47
+ }
48
+ const pluginTransform = plugin.transform!.bind(null as unknown as TransformPluginContext);
49
+ return async ({ attributes, content, filename = '' }) => {
50
+ const lang = attributes.lang as string;
51
+ if (!supportedStyleLangs.includes(lang)) return;
52
+ const moduleId = `${filename}.${lang}`;
53
+ const transformResult: TransformResult = (await pluginTransform(
54
+ content,
55
+ moduleId
56
+ )) as TransformResult;
57
+ // patch sourcemap source to point back to original filename
58
+ if (transformResult.map?.sources?.[0] === moduleId) {
59
+ transformResult.map.sources[0] = filename;
60
+ }
61
+ return {
62
+ code: transformResult.code,
63
+ map: transformResult.map ?? undefined
64
+ };
65
+ };
66
+ }
67
+
68
+ function createVitePreprocessorGroup(config: ResolvedConfig): PreprocessorGroup {
69
+ return {
70
+ markup({ content, filename }) {
71
+ return preprocess(
72
+ content,
73
+ {
74
+ script: createViteScriptPreprocessor(),
75
+ style: createViteStylePreprocessor(config)
76
+ },
77
+ { filename }
78
+ );
79
+ }
80
+ } as PreprocessorGroup;
81
+ }
82
+
83
+ /**
84
+ * this appends a *{} rule to component styles to force the svelte compiler to add style classes to all nodes
85
+ * That means adding/removing class rules from <style> node won't trigger js updates as the scope classes are not changed
86
+ *
87
+ * only used during dev with enabled css hmr
88
+ */
89
+ function createInjectScopeEverythingRulePreprocessorGroup(): PreprocessorGroup {
90
+ return {
91
+ style({ content, filename }) {
92
+ const s = new MagicString(content);
93
+ s.append(' *{}');
94
+ return {
95
+ code: s.toString(),
96
+ map: s.generateDecodedMap({ source: filename, hires: true })
97
+ };
98
+ }
99
+ };
100
+ }
101
+
102
+ function buildExtraPreprocessors(options: ResolvedOptions, config: ResolvedConfig) {
103
+ const prependPreprocessors: PreprocessorGroup[] = [];
104
+ const appendPreprocessors: PreprocessorGroup[] = [];
105
+
106
+ if (options.experimental?.useVitePreprocess) {
107
+ log.debug('adding vite preprocessor');
108
+ prependPreprocessors.push(createVitePreprocessorGroup(config));
109
+ }
110
+
111
+ // @ts-ignore
112
+ const pluginsWithPreprocessorsDeprecated = config.plugins.filter((p) => p?.sveltePreprocess);
113
+ if (pluginsWithPreprocessorsDeprecated.length > 0) {
114
+ log.warn(
115
+ `The following plugins use the deprecated 'plugin.sveltePreprocess' field. Please contact their maintainers and ask them to move it to 'plugin.api.sveltePreprocess': ${pluginsWithPreprocessorsDeprecated
116
+ .map((p) => p.name)
117
+ .join(', ')}`
118
+ );
119
+ // patch plugin to avoid breaking
120
+ pluginsWithPreprocessorsDeprecated.forEach((p) => {
121
+ if (!p.api) {
122
+ p.api = {};
123
+ }
124
+ if (p.api.sveltePreprocess === undefined) {
125
+ // @ts-ignore
126
+ p.api.sveltePreprocess = p.sveltePreprocess;
127
+ } else {
128
+ log.error(
129
+ `ignoring plugin.sveltePreprocess of ${p.name} because it already defined plugin.api.sveltePreprocess.`
130
+ );
131
+ }
132
+ });
133
+ }
134
+
135
+ const pluginsWithPreprocessors: Plugin[] = config.plugins.filter((p) => p?.api?.sveltePreprocess);
136
+ const ignored: Plugin[] = [],
137
+ included: Plugin[] = [];
138
+ for (const p of pluginsWithPreprocessors) {
139
+ if (
140
+ options.ignorePluginPreprocessors === true ||
141
+ (Array.isArray(options.ignorePluginPreprocessors) &&
142
+ options.ignorePluginPreprocessors?.includes(p.name))
143
+ ) {
144
+ ignored.push(p);
145
+ } else {
146
+ included.push(p);
147
+ }
148
+ }
149
+ if (ignored.length > 0) {
150
+ log.debug(
151
+ `Ignoring svelte preprocessors defined by these vite plugins: ${ignored
152
+ .map((p) => p.name)
153
+ .join(', ')}`
154
+ );
155
+ }
156
+ if (included.length > 0) {
157
+ log.debug(
158
+ `Adding svelte preprocessors defined by these vite plugins: ${included
159
+ .map((p) => p.name)
160
+ .join(', ')}`
161
+ );
162
+ appendPreprocessors.push(...pluginsWithPreprocessors.map((p) => p.api.sveltePreprocess));
163
+ }
164
+
165
+ if (options.hot && options.emitCss) {
166
+ appendPreprocessors.push(createInjectScopeEverythingRulePreprocessorGroup());
167
+ }
168
+
169
+ return { prependPreprocessors, appendPreprocessors };
170
+ }
171
+
172
+ export function addExtraPreprocessors(options: ResolvedOptions, config: ResolvedConfig) {
173
+ const { prependPreprocessors, appendPreprocessors } = buildExtraPreprocessors(options, config);
174
+ if (prependPreprocessors.length > 0 || appendPreprocessors.length > 0) {
175
+ if (!options.preprocess) {
176
+ options.preprocess = [...prependPreprocessors, ...appendPreprocessors];
177
+ } else if (Array.isArray(options.preprocess)) {
178
+ options.preprocess.unshift(...prependPreprocessors);
179
+ options.preprocess.push(...appendPreprocessors);
180
+ } else {
181
+ options.preprocess = [...prependPreprocessors, options.preprocess, ...appendPreprocessors];
182
+ }
183
+ }
184
+ const generateMissingSourceMaps = !!options.experimental?.generateMissingPreprocessorSourcemaps;
185
+ if (options.preprocess && generateMissingSourceMaps) {
186
+ options.preprocess = Array.isArray(options.preprocess)
187
+ ? options.preprocess.map((p, i) => validateSourceMapOutputWrapper(p, i))
188
+ : validateSourceMapOutputWrapper(options.preprocess, 0);
189
+ }
190
+ }
191
+
192
+ function validateSourceMapOutputWrapper(group: PreprocessorGroup, i: number): PreprocessorGroup {
193
+ const wrapper: PreprocessorGroup = {};
194
+
195
+ for (const [processorType, processorFn] of Object.entries(group) as Array<
196
+ // eslint-disable-next-line no-unused-vars
197
+ [keyof PreprocessorGroup, (options: { filename?: string; content: string }) => Processed]
198
+ >) {
199
+ wrapper[processorType] = async (options) => {
200
+ const result = await processorFn(options);
201
+
202
+ if (result && result.code !== options.content) {
203
+ let invalidMap = false;
204
+ if (!result.map) {
205
+ invalidMap = true;
206
+ log.warn.enabled &&
207
+ log.warn.once(
208
+ `preprocessor at index ${i} did not return a sourcemap for ${processorType} transform`,
209
+ {
210
+ filename: options.filename,
211
+ type: processorType,
212
+ processor: processorFn.toString()
213
+ }
214
+ );
215
+ } else if ((result.map as any)?.mappings === '') {
216
+ invalidMap = true;
217
+ log.warn.enabled &&
218
+ log.warn.once(
219
+ `preprocessor at index ${i} returned an invalid empty sourcemap for ${processorType} transform`,
220
+ {
221
+ filename: options.filename,
222
+ type: processorType,
223
+ processor: processorFn.toString()
224
+ }
225
+ );
226
+ }
227
+ if (invalidMap) {
228
+ try {
229
+ const map = await buildSourceMap(options.content, result.code, options.filename);
230
+ if (map) {
231
+ log.debug.enabled &&
232
+ log.debug(
233
+ `adding generated sourcemap to preprocesor result for ${options.filename}`
234
+ );
235
+ result.map = map;
236
+ }
237
+ } catch (e) {
238
+ log.error(`failed to build sourcemap`, e);
239
+ }
240
+ }
241
+ }
242
+ return result;
243
+ };
244
+ }
245
+
246
+ return wrapper;
247
+ }
@@ -0,0 +1,35 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ // @ts-ignore
4
+ import relative from 'require-relative';
5
+
6
+ export function resolveViaPackageJsonSvelte(importee: string, importer?: string): string | void {
7
+ if (importer && isBareImport(importee)) {
8
+ const importeePkgFile = relative.resolve(`${importee}/package.json`, path.dirname(importer));
9
+ const importeePkg = JSON.parse(fs.readFileSync(importeePkgFile, { encoding: 'utf-8' }));
10
+ if (importeePkg.svelte) {
11
+ return path.resolve(path.dirname(importeePkgFile), importeePkg.svelte);
12
+ }
13
+ }
14
+ }
15
+
16
+ function isBareImport(importee: string): boolean {
17
+ if (
18
+ !importee ||
19
+ importee[0] === '.' ||
20
+ importee[0] === '\0' ||
21
+ importee.includes(':') ||
22
+ path.isAbsolute(importee)
23
+ ) {
24
+ return false;
25
+ }
26
+ const parts = importee.split('/');
27
+ switch (parts.length) {
28
+ case 1:
29
+ return true;
30
+ case 2:
31
+ return parts[0].startsWith('@');
32
+ default:
33
+ return false;
34
+ }
35
+ }
@@ -0,0 +1,58 @@
1
+ import MagicString, { MagicStringOptions } from 'magic-string';
2
+ import { log } from './log';
3
+
4
+ export async function buildMagicString(
5
+ from: string,
6
+ to: string,
7
+ options?: MagicStringOptions
8
+ ): Promise<MagicString | null> {
9
+ let diff_match_patch, DIFF_DELETE: number, DIFF_INSERT: number;
10
+ try {
11
+ const dmpPkg = await import('diff-match-patch');
12
+ diff_match_patch = dmpPkg.diff_match_patch;
13
+ DIFF_INSERT = dmpPkg.DIFF_INSERT;
14
+ DIFF_DELETE = dmpPkg.DIFF_DELETE;
15
+ } catch (e) {
16
+ log.error.once(
17
+ 'Failed to import optional dependency "diff-match-patch". Please install it to enable generated sourcemaps.'
18
+ );
19
+ return null;
20
+ }
21
+
22
+ const dmp = new diff_match_patch();
23
+ const diffs = dmp.diff_main(from, to);
24
+ dmp.diff_cleanupSemantic(diffs);
25
+ const m = new MagicString(from, options);
26
+ let pos = 0;
27
+ for (let i = 0; i < diffs.length; i++) {
28
+ const diff = diffs[i];
29
+ const nextDiff = diffs[i + 1];
30
+ if (diff[0] === DIFF_DELETE) {
31
+ if (nextDiff?.[0] === DIFF_INSERT) {
32
+ // delete followed by insert, use overwrite and skip ahead
33
+ m.overwrite(pos, pos + diff[1].length, nextDiff[1]);
34
+ i++;
35
+ } else {
36
+ m.remove(pos, pos + diff[1].length);
37
+ }
38
+ pos += diff[1].length;
39
+ } else if (diff[0] === DIFF_INSERT) {
40
+ if (nextDiff) {
41
+ m.appendRight(pos, diff[1]);
42
+ } else {
43
+ m.append(diff[1]);
44
+ }
45
+ } else {
46
+ // unchanged block, advance pos
47
+ pos += diff[1].length;
48
+ }
49
+ }
50
+ // at this point m.toString() === to
51
+ return m;
52
+ }
53
+
54
+ export async function buildSourceMap(from: string, to: string, filename?: string) {
55
+ // @ts-ignore
56
+ const m = await buildMagicString(from, to, { filename });
57
+ return m ? m.generateDecodedMap({ source: filename, hires: true, includeContent: false }) : null;
58
+ }
@@ -0,0 +1,83 @@
1
+ import { SvelteRequest } from './id';
2
+ import { Code, CompileData } from './compile';
3
+
4
+ export class VitePluginSvelteCache {
5
+ private _css = new Map<string, Code>();
6
+ private _js = new Map<string, Code>();
7
+ private _dependencies = new Map<string, string[]>();
8
+ private _dependants = new Map<string, Set<string>>();
9
+
10
+ public update(compileData: CompileData) {
11
+ this.updateCSS(compileData);
12
+ this.updateJS(compileData);
13
+ this.updateDependencies(compileData);
14
+ }
15
+
16
+ private updateCSS(compileData: CompileData) {
17
+ this._css.set(compileData.normalizedFilename, compileData.compiled.css);
18
+ }
19
+
20
+ private updateJS(compileData: CompileData) {
21
+ if (!compileData.ssr) {
22
+ // do not cache SSR js
23
+ this._js.set(compileData.normalizedFilename, compileData.compiled.js);
24
+ }
25
+ }
26
+
27
+ private updateDependencies(compileData: CompileData) {
28
+ const id = compileData.normalizedFilename;
29
+ const prevDependencies = this._dependencies.get(id) || [];
30
+ const dependencies = compileData.dependencies;
31
+ this._dependencies.set(id, dependencies);
32
+ const removed = prevDependencies.filter((d) => !dependencies.includes(d));
33
+ const added = dependencies.filter((d) => !prevDependencies.includes(d));
34
+ added.forEach((d) => {
35
+ if (!this._dependants.has(d)) {
36
+ this._dependants.set(d, new Set<string>());
37
+ }
38
+ this._dependants.get(d)!.add(compileData.filename);
39
+ });
40
+ removed.forEach((d) => {
41
+ this._dependants.get(d)!.delete(compileData.filename);
42
+ });
43
+ }
44
+
45
+ public remove(svelteRequest: SvelteRequest): boolean {
46
+ const id = svelteRequest.normalizedFilename;
47
+ let removed = false;
48
+ if (this._js.delete(id)) {
49
+ removed = true;
50
+ }
51
+ if (this._css.delete(id)) {
52
+ removed = true;
53
+ }
54
+ const dependencies = this._dependencies.get(id);
55
+ if (dependencies) {
56
+ removed = true;
57
+ dependencies.forEach((d) => {
58
+ const dependants = this._dependants.get(d);
59
+ if (dependants && dependants.has(svelteRequest.filename)) {
60
+ dependants.delete(svelteRequest.filename);
61
+ }
62
+ });
63
+ this._dependencies.delete(id);
64
+ }
65
+ return removed;
66
+ }
67
+
68
+ public getCSS(svelteRequest: SvelteRequest) {
69
+ return this._css.get(svelteRequest.normalizedFilename);
70
+ }
71
+
72
+ public getJS(svelteRequest: SvelteRequest) {
73
+ if (!svelteRequest.ssr) {
74
+ // SSR js isn't cached
75
+ return this._js.get(svelteRequest.normalizedFilename);
76
+ }
77
+ }
78
+
79
+ public getDependants(path: string): string[] {
80
+ const dependants = this._dependants.get(path);
81
+ return dependants ? [...dependants] : [];
82
+ }
83
+ }
@@ -0,0 +1,106 @@
1
+ import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
2
+ import fs from 'fs';
3
+ import { log } from './log';
4
+ import { IdParser } from './id';
5
+ import { ResolvedOptions } from './options';
6
+ import { knownSvelteConfigNames } from './load-svelte-config';
7
+ import path from 'path';
8
+ import { FSWatcher } from 'vite';
9
+
10
+ export function setupWatchers(
11
+ options: ResolvedOptions,
12
+ cache: VitePluginSvelteCache,
13
+ requestParser: IdParser
14
+ ) {
15
+ const { server, configFile: svelteConfigFile } = options;
16
+ if (!server) {
17
+ return;
18
+ }
19
+ const { watcher, ws } = server;
20
+ const { root, server: serverConfig } = server.config;
21
+
22
+ const emitChangeEventOnDependants = (filename: string) => {
23
+ const dependants = cache.getDependants(filename);
24
+ dependants.forEach((dependant) => {
25
+ if (fs.existsSync(dependant)) {
26
+ log.debug(
27
+ `emitting virtual change event for "${dependant}" because depdendency "${filename}" changed`
28
+ );
29
+ watcher.emit('change', dependant);
30
+ }
31
+ });
32
+ };
33
+
34
+ const removeUnlinkedFromCache = (filename: string) => {
35
+ const svelteRequest = requestParser(filename, false);
36
+ if (svelteRequest) {
37
+ const removedFromCache = cache.remove(svelteRequest);
38
+ if (removedFromCache) {
39
+ log.debug(`cleared VitePluginSvelteCache for deleted file ${filename}`);
40
+ }
41
+ }
42
+ };
43
+
44
+ const triggerViteRestart = (filename: string) => {
45
+ if (serverConfig.middlewareMode) {
46
+ // in middlewareMode we can't restart the server automatically
47
+ // show the user an overlay instead
48
+ const message =
49
+ 'Svelte config change detected, restart your dev process to apply the changes.';
50
+ log.info(message, filename);
51
+ ws.send({
52
+ type: 'error',
53
+ err: { message, stack: '', plugin: 'vite-plugin-svelte', id: filename }
54
+ });
55
+ } else {
56
+ log.info(`svelte config changed: restarting vite server. - file: ${filename}`);
57
+ server.restart(!!options.experimental?.prebundleSvelteLibraries);
58
+ }
59
+ };
60
+
61
+ const possibleSvelteConfigs = knownSvelteConfigNames.map((cfg) => path.join(root, cfg));
62
+ const restartOnConfigAdd = (filename: string) => {
63
+ if (possibleSvelteConfigs.includes(filename)) {
64
+ triggerViteRestart(filename);
65
+ }
66
+ };
67
+
68
+ const restartOnConfigChange = (filename: string) => {
69
+ if (filename === svelteConfigFile) {
70
+ triggerViteRestart(filename);
71
+ }
72
+ };
73
+
74
+ // collection of watcher listeners by event
75
+ const listenerCollection = {
76
+ add: [] as Array<Function>,
77
+ change: [emitChangeEventOnDependants],
78
+ unlink: [removeUnlinkedFromCache, emitChangeEventOnDependants]
79
+ };
80
+ if (svelteConfigFile) {
81
+ listenerCollection.change.push(restartOnConfigChange);
82
+ listenerCollection.unlink.push(restartOnConfigChange);
83
+ } else {
84
+ listenerCollection.add.push(restartOnConfigAdd);
85
+ }
86
+
87
+ Object.entries(listenerCollection).forEach(([evt, listeners]) => {
88
+ if (listeners.length > 0) {
89
+ watcher.on(evt, (filename) => listeners.forEach((listener) => listener(filename)));
90
+ }
91
+ });
92
+ }
93
+ // taken from vite utils
94
+ export function ensureWatchedFile(watcher: FSWatcher, file: string | null, root: string): void {
95
+ if (
96
+ file &&
97
+ // only need to watch if out of root
98
+ !file.startsWith(root + '/') &&
99
+ // some rollup plugins use null bytes for private resolved Ids
100
+ !file.includes('\0') &&
101
+ fs.existsSync(file)
102
+ ) {
103
+ // resolve file to normalized system path
104
+ watcher.add(path.resolve(file));
105
+ }
106
+ }
package/CHANGELOG.md DELETED
@@ -1,19 +0,0 @@
1
- # @sveltejs/vite-plugin-svelte
2
-
3
- ## 1.0.0-next.3
4
-
5
- ### Patch Changes
6
-
7
- - c3cf3f3: Bump deps
8
-
9
- ## 1.0.0-next.2
10
-
11
- ### Patch Changes
12
-
13
- - Include index.js in package
14
-
15
- ## 1.0.0-next.1
16
-
17
- ### Patch Changes
18
-
19
- - 865df9d: Bump version to allow publishing via pnpm