@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.
- package/LICENSE +18 -4
- package/README.md +28 -0
- package/dist/index.cjs +1581 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +158 -0
- package/dist/index.js +1565 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -32
- package/src/handle-hot-update.ts +111 -0
- package/src/index.ts +225 -0
- package/src/utils/__tests__/dependencies.spec.ts +29 -0
- package/src/utils/__tests__/sourcemap.spec.ts +24 -0
- package/src/utils/compile.ts +148 -0
- package/src/utils/constants.ts +20 -0
- package/src/utils/dependencies.ts +223 -0
- package/src/utils/error.ts +92 -0
- package/src/utils/esbuild.ts +102 -0
- package/src/utils/hash.ts +32 -0
- package/src/utils/id.ts +135 -0
- package/src/utils/load-svelte-config.ts +108 -0
- package/src/utils/log.ts +170 -0
- package/src/utils/options.ts +513 -0
- package/src/utils/preprocess.ts +247 -0
- package/src/utils/resolve.ts +35 -0
- package/src/utils/sourcemap.ts +58 -0
- package/src/utils/vite-plugin-svelte-cache.ts +83 -0
- package/src/utils/watch.ts +106 -0
- package/CHANGELOG.md +0 -19
- package/index.js +0 -570
package/src/index.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
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
|
+
|
|
23
|
+
export function svelte(inlineOptions?: Partial<Options>): Plugin {
|
|
24
|
+
if (process.env.DEBUG != null) {
|
|
25
|
+
log.setLevel('debug');
|
|
26
|
+
}
|
|
27
|
+
validateInlineOptions(inlineOptions);
|
|
28
|
+
const cache = new VitePluginSvelteCache();
|
|
29
|
+
const pkg_export_errors = new Set();
|
|
30
|
+
// updated in configResolved hook
|
|
31
|
+
let requestParser: IdParser;
|
|
32
|
+
let options: ResolvedOptions;
|
|
33
|
+
let viteConfig: ResolvedConfig;
|
|
34
|
+
/* eslint-disable no-unused-vars */
|
|
35
|
+
let compileSvelte: (
|
|
36
|
+
svelteRequest: SvelteRequest,
|
|
37
|
+
code: string,
|
|
38
|
+
options: Partial<ResolvedOptions>
|
|
39
|
+
) => Promise<CompileData>;
|
|
40
|
+
/* eslint-enable no-unused-vars */
|
|
41
|
+
|
|
42
|
+
let resolvedSvelteSSR: Promise<PartialResolvedId | null>;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
name: 'vite-plugin-svelte',
|
|
46
|
+
// make sure our resolver runs before vite internal resolver to resolve svelte field correctly
|
|
47
|
+
enforce: 'pre',
|
|
48
|
+
async config(config, configEnv): Promise<Partial<UserConfig>> {
|
|
49
|
+
// setup logger
|
|
50
|
+
if (process.env.DEBUG) {
|
|
51
|
+
log.setLevel('debug');
|
|
52
|
+
} else if (config.logLevel) {
|
|
53
|
+
log.setLevel(config.logLevel);
|
|
54
|
+
}
|
|
55
|
+
// @ts-expect-error temporarily lend the options variable until fixed in configResolved
|
|
56
|
+
options = await preResolveOptions(inlineOptions, config, configEnv);
|
|
57
|
+
// extra vite config
|
|
58
|
+
const extraViteConfig = buildExtraViteConfig(options, config, configEnv);
|
|
59
|
+
log.debug('additional vite config', extraViteConfig);
|
|
60
|
+
return extraViteConfig;
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
async configResolved(config) {
|
|
64
|
+
options = resolveOptions(options, config);
|
|
65
|
+
patchResolvedViteConfig(config, options);
|
|
66
|
+
requestParser = buildIdParser(options);
|
|
67
|
+
compileSvelte = createCompileSvelte(options);
|
|
68
|
+
viteConfig = config;
|
|
69
|
+
log.debug('resolved options', options);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
configureServer(server) {
|
|
73
|
+
// eslint-disable-next-line no-unused-vars
|
|
74
|
+
options.server = server;
|
|
75
|
+
setupWatchers(options, cache, requestParser);
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
load(id, opts) {
|
|
79
|
+
// @ts-expect-error anticipate vite changing second parameter as options object
|
|
80
|
+
// see https://github.com/vitejs/vite/discussions/5109
|
|
81
|
+
const ssr: boolean = opts === true || opts?.ssr;
|
|
82
|
+
const svelteRequest = requestParser(id, !!ssr);
|
|
83
|
+
if (svelteRequest) {
|
|
84
|
+
const { filename, query } = svelteRequest;
|
|
85
|
+
// virtual css module
|
|
86
|
+
if (query.svelte && query.type === 'style') {
|
|
87
|
+
const css = cache.getCSS(svelteRequest);
|
|
88
|
+
if (css) {
|
|
89
|
+
log.debug(`load returns css for ${filename}`);
|
|
90
|
+
return css;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// prevent vite asset plugin from loading files as url that should be compiled in transform
|
|
94
|
+
if (viteConfig.assetsInclude(filename)) {
|
|
95
|
+
log.debug(`load returns raw content for ${filename}`);
|
|
96
|
+
return fs.readFileSync(filename, 'utf-8');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
async resolveId(importee, importer, opts) {
|
|
102
|
+
const ssr = !!opts?.ssr;
|
|
103
|
+
const svelteRequest = requestParser(importee, ssr);
|
|
104
|
+
if (svelteRequest?.query.svelte) {
|
|
105
|
+
if (svelteRequest.query.type === 'style') {
|
|
106
|
+
// return cssId with root prefix so postcss pipeline of vite finds the directory correctly
|
|
107
|
+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/14
|
|
108
|
+
log.debug(`resolveId resolved virtual css module ${svelteRequest.cssId}`);
|
|
109
|
+
return svelteRequest.cssId;
|
|
110
|
+
}
|
|
111
|
+
log.debug(`resolveId resolved ${importee}`);
|
|
112
|
+
return importee; // query with svelte tag, an id we generated, no need for further analysis
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (ssr && importee === 'svelte') {
|
|
116
|
+
if (!resolvedSvelteSSR) {
|
|
117
|
+
resolvedSvelteSSR = this.resolve('svelte/ssr', undefined, { skipSelf: true }).then(
|
|
118
|
+
(svelteSSR) => {
|
|
119
|
+
log.debug('resolved svelte to svelte/ssr');
|
|
120
|
+
return svelteSSR;
|
|
121
|
+
},
|
|
122
|
+
(err) => {
|
|
123
|
+
log.debug(
|
|
124
|
+
'failed to resolve svelte to svelte/ssr. Update svelte to a version that exports it',
|
|
125
|
+
err
|
|
126
|
+
);
|
|
127
|
+
return null; // returning null here leads to svelte getting resolved regularly
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
return resolvedSvelteSSR;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const resolved = resolveViaPackageJsonSvelte(importee, importer);
|
|
136
|
+
if (resolved) {
|
|
137
|
+
log.debug(`resolveId resolved ${resolved} via package.json svelte field of ${importee}`);
|
|
138
|
+
return resolved;
|
|
139
|
+
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
switch (err.code) {
|
|
142
|
+
case 'ERR_PACKAGE_PATH_NOT_EXPORTED':
|
|
143
|
+
pkg_export_errors.add(importee);
|
|
144
|
+
return null;
|
|
145
|
+
case 'MODULE_NOT_FOUND':
|
|
146
|
+
return null;
|
|
147
|
+
default:
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
async transform(code, id, opts) {
|
|
154
|
+
const ssr = !!opts?.ssr;
|
|
155
|
+
const svelteRequest = requestParser(id, ssr);
|
|
156
|
+
if (!svelteRequest) {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
const { filename, query } = svelteRequest;
|
|
160
|
+
|
|
161
|
+
if (query.svelte) {
|
|
162
|
+
if (query.type === 'style') {
|
|
163
|
+
const css = cache.getCSS(svelteRequest);
|
|
164
|
+
if (css) {
|
|
165
|
+
log.debug(`transform returns css for ${filename}`);
|
|
166
|
+
return css; // TODO return code arg instead? it's the code from load hook.
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
log.error('failed to transform tagged svelte request', svelteRequest);
|
|
170
|
+
throw new Error(`failed to transform tagged svelte request for id ${id}`);
|
|
171
|
+
}
|
|
172
|
+
let compileData;
|
|
173
|
+
try {
|
|
174
|
+
compileData = await compileSvelte(svelteRequest, code, options);
|
|
175
|
+
} catch (e) {
|
|
176
|
+
throw toRollupError(e);
|
|
177
|
+
}
|
|
178
|
+
logCompilerWarnings(compileData.compiled.warnings, options);
|
|
179
|
+
cache.update(compileData);
|
|
180
|
+
if (compileData.dependencies?.length && options.server) {
|
|
181
|
+
compileData.dependencies.forEach((d) => {
|
|
182
|
+
ensureWatchedFile(options.server!.watcher, d, options.root);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
log.debug(`transform returns compiled js for ${filename}`);
|
|
186
|
+
return compileData.compiled.js;
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
handleHotUpdate(ctx: HmrContext): void | Promise<Array<ModuleNode> | void> {
|
|
190
|
+
if (!options.hot || !options.emitCss) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const svelteRequest = requestParser(ctx.file, false, ctx.timestamp);
|
|
194
|
+
if (svelteRequest) {
|
|
195
|
+
return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options);
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* All resolutions done; display warnings wrt `package.json` access.
|
|
201
|
+
*/
|
|
202
|
+
// TODO generateBundle isn't called by vite, is buildEnd enough or should it be logged once per violation in resolve
|
|
203
|
+
buildEnd() {
|
|
204
|
+
if (pkg_export_errors.size > 0) {
|
|
205
|
+
log.warn(
|
|
206
|
+
`The following packages did not export their \`package.json\` file so we could not check the "svelte" field. If you had difficulties importing svelte components from a package, then please contact the author and ask them to export the package.json file.`,
|
|
207
|
+
Array.from(pkg_export_errors, (s) => `- ${s}`).join('\n')
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export {
|
|
215
|
+
Options,
|
|
216
|
+
Preprocessor,
|
|
217
|
+
PreprocessorGroup,
|
|
218
|
+
CompileOptions,
|
|
219
|
+
CssHashGetter,
|
|
220
|
+
Arrayable,
|
|
221
|
+
MarkupPreprocessor,
|
|
222
|
+
ModuleFormat,
|
|
223
|
+
Processed,
|
|
224
|
+
Warning
|
|
225
|
+
} from './utils/options';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { findRootSvelteDependencies } from '../dependencies';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
describe('dependencies', () => {
|
|
5
|
+
describe('findRootSvelteDependencies', () => {
|
|
6
|
+
it('should find svelte dependencies in packages/e2e-test/hmr', async () => {
|
|
7
|
+
const deps = findRootSvelteDependencies(path.resolve('packages/e2e-tests/hmr'));
|
|
8
|
+
expect(deps).toHaveLength(1);
|
|
9
|
+
expect(deps[0].name).toBe('e2e-test-dep-svelte-simple');
|
|
10
|
+
expect(deps[0].path).toEqual([]);
|
|
11
|
+
});
|
|
12
|
+
it('should find nested svelte dependencies in packages/e2e-test/package-json-svelte-field', async () => {
|
|
13
|
+
const deps = findRootSvelteDependencies(
|
|
14
|
+
path.resolve('packages/e2e-tests/package-json-svelte-field')
|
|
15
|
+
);
|
|
16
|
+
expect(deps).toHaveLength(3);
|
|
17
|
+
const hybrid = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-hybrid');
|
|
18
|
+
expect(hybrid).toBeTruthy();
|
|
19
|
+
expect(hybrid.path).toHaveLength(0);
|
|
20
|
+
const nested = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-nested');
|
|
21
|
+
expect(nested).toBeTruthy();
|
|
22
|
+
expect(nested.path).toHaveLength(0);
|
|
23
|
+
const simple = deps.find((dep) => dep.name === 'e2e-test-dep-svelte-simple');
|
|
24
|
+
expect(simple).toBeTruthy();
|
|
25
|
+
expect(simple.path).toHaveLength(1);
|
|
26
|
+
expect(simple.path[0]).toBe('e2e-test-dep-svelte-nested');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { buildMagicString, buildSourceMap } from '../sourcemap';
|
|
2
|
+
|
|
3
|
+
describe('sourcemap', () => {
|
|
4
|
+
describe('buildMagicString', () => {
|
|
5
|
+
it('should return a valid magic string', async () => {
|
|
6
|
+
const from = 'h1{color: blue}\nh2{color: green}\nh3{color: red}\n';
|
|
7
|
+
const to = 'h1{color: blue}\ndiv{color: white}\nh3{color: red}\nh2{color: green}\n';
|
|
8
|
+
const m = await buildMagicString(from, to);
|
|
9
|
+
expect(m).toBeDefined();
|
|
10
|
+
expect(m.original).toBe(from);
|
|
11
|
+
expect(m.toString()).toBe(to);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('buildSourceMap', () => {
|
|
15
|
+
it('should return a map with mappings and filename', async () => {
|
|
16
|
+
const map = await buildSourceMap('foo', 'bar', 'foo.txt');
|
|
17
|
+
expect(map).toBeDefined();
|
|
18
|
+
expect(map.mappings).toBeDefined();
|
|
19
|
+
expect(map.mappings[0]).toBeDefined();
|
|
20
|
+
expect(map.mappings[0][0]).toBeDefined();
|
|
21
|
+
expect(map.sources[0]).toBe('foo.txt');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { CompileOptions, ResolvedOptions } from './options';
|
|
2
|
+
import { compile, preprocess, walk } from 'svelte/compiler';
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import { createMakeHot } from 'svelte-hmr';
|
|
5
|
+
import { SvelteRequest } from './id';
|
|
6
|
+
import { safeBase64Hash } from './hash';
|
|
7
|
+
import { log } from './log';
|
|
8
|
+
|
|
9
|
+
const _createCompileSvelte = (makeHot: Function) =>
|
|
10
|
+
async function compileSvelte(
|
|
11
|
+
svelteRequest: SvelteRequest,
|
|
12
|
+
code: string,
|
|
13
|
+
options: Partial<ResolvedOptions>
|
|
14
|
+
): Promise<CompileData> {
|
|
15
|
+
const { filename, normalizedFilename, cssId, ssr } = svelteRequest;
|
|
16
|
+
const { emitCss = true } = options;
|
|
17
|
+
const dependencies = [];
|
|
18
|
+
|
|
19
|
+
const compileOptions: CompileOptions = {
|
|
20
|
+
...options.compilerOptions,
|
|
21
|
+
filename,
|
|
22
|
+
generate: ssr ? 'ssr' : 'dom'
|
|
23
|
+
};
|
|
24
|
+
if (options.hot && options.emitCss) {
|
|
25
|
+
const hash = `s-${safeBase64Hash(normalizedFilename)}`;
|
|
26
|
+
log.debug(`setting cssHash ${hash} for ${normalizedFilename}`);
|
|
27
|
+
compileOptions.cssHash = () => hash;
|
|
28
|
+
}
|
|
29
|
+
if (ssr && compileOptions.enableSourcemap !== false) {
|
|
30
|
+
if (typeof compileOptions.enableSourcemap === 'object') {
|
|
31
|
+
compileOptions.enableSourcemap.css = false;
|
|
32
|
+
} else {
|
|
33
|
+
compileOptions.enableSourcemap = { js: true, css: false };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let preprocessed;
|
|
38
|
+
|
|
39
|
+
if (options.preprocess) {
|
|
40
|
+
preprocessed = await preprocess(code, options.preprocess, { filename });
|
|
41
|
+
if (preprocessed.dependencies) dependencies.push(...preprocessed.dependencies);
|
|
42
|
+
if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
|
|
43
|
+
}
|
|
44
|
+
const finalCode = preprocessed ? preprocessed.code : code;
|
|
45
|
+
const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
|
|
46
|
+
filename,
|
|
47
|
+
code: finalCode,
|
|
48
|
+
compileOptions
|
|
49
|
+
});
|
|
50
|
+
if (dynamicCompileOptions && log.debug.enabled) {
|
|
51
|
+
log.debug(
|
|
52
|
+
`dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
const finalCompileOptions = dynamicCompileOptions
|
|
56
|
+
? {
|
|
57
|
+
...compileOptions,
|
|
58
|
+
...dynamicCompileOptions
|
|
59
|
+
}
|
|
60
|
+
: compileOptions;
|
|
61
|
+
const compiled = compile(finalCode, finalCompileOptions);
|
|
62
|
+
|
|
63
|
+
if (emitCss && compiled.css.code) {
|
|
64
|
+
// TODO properly update sourcemap?
|
|
65
|
+
compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// only apply hmr when not in ssr context and hot options are set
|
|
69
|
+
if (!ssr && makeHot) {
|
|
70
|
+
compiled.js.code = makeHot({
|
|
71
|
+
id: filename,
|
|
72
|
+
compiledCode: compiled.js.code,
|
|
73
|
+
hotOptions: options.hot,
|
|
74
|
+
compiled,
|
|
75
|
+
originalCode: code,
|
|
76
|
+
compileOptions: finalCompileOptions
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
compiled.js.dependencies = dependencies;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
filename,
|
|
84
|
+
normalizedFilename,
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
compiled,
|
|
87
|
+
ssr,
|
|
88
|
+
dependencies
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function buildMakeHot(options: ResolvedOptions) {
|
|
93
|
+
const needsMakeHot = options.hot !== false && options.isServe && !options.isProduction;
|
|
94
|
+
if (needsMakeHot) {
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
const hotApi = options?.hot?.hotApi;
|
|
97
|
+
// @ts-ignore
|
|
98
|
+
const adapter = options?.hot?.adapter;
|
|
99
|
+
return createMakeHot({
|
|
100
|
+
walk,
|
|
101
|
+
hotApi,
|
|
102
|
+
adapter,
|
|
103
|
+
hotOptions: { noOverlay: true, ...(options.hot as object) }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function createCompileSvelte(options: ResolvedOptions) {
|
|
109
|
+
const makeHot = buildMakeHot(options);
|
|
110
|
+
return _createCompileSvelte(makeHot);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface Code {
|
|
114
|
+
code: string;
|
|
115
|
+
map?: any;
|
|
116
|
+
dependencies?: any[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface Compiled {
|
|
120
|
+
js: Code;
|
|
121
|
+
css: Code;
|
|
122
|
+
ast: any; // TODO type
|
|
123
|
+
warnings: any[]; // TODO type
|
|
124
|
+
vars: {
|
|
125
|
+
name: string;
|
|
126
|
+
export_name: string;
|
|
127
|
+
injected: boolean;
|
|
128
|
+
module: boolean;
|
|
129
|
+
mutated: boolean;
|
|
130
|
+
reassigned: boolean;
|
|
131
|
+
referenced: boolean;
|
|
132
|
+
writable: boolean;
|
|
133
|
+
referenced_from_script: boolean;
|
|
134
|
+
}[];
|
|
135
|
+
stats: {
|
|
136
|
+
timings: {
|
|
137
|
+
total: number;
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface CompileData {
|
|
143
|
+
filename: string;
|
|
144
|
+
normalizedFilename: string;
|
|
145
|
+
compiled: Compiled;
|
|
146
|
+
ssr: boolean | undefined;
|
|
147
|
+
dependencies: string[];
|
|
148
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const VITE_RESOLVE_MAIN_FIELDS = ['module', 'jsnext:main', 'jsnext'];
|
|
2
|
+
|
|
3
|
+
export const SVELTE_RESOLVE_MAIN_FIELDS = ['svelte', ...VITE_RESOLVE_MAIN_FIELDS];
|
|
4
|
+
|
|
5
|
+
export const SVELTE_IMPORTS = [
|
|
6
|
+
'svelte/animate',
|
|
7
|
+
'svelte/easing',
|
|
8
|
+
'svelte/internal',
|
|
9
|
+
'svelte/motion',
|
|
10
|
+
'svelte/ssr',
|
|
11
|
+
'svelte/store',
|
|
12
|
+
'svelte/transition',
|
|
13
|
+
'svelte'
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
export const SVELTE_HMR_IMPORTS = [
|
|
17
|
+
'svelte-hmr/runtime/hot-api-esm.js',
|
|
18
|
+
'svelte-hmr/runtime/proxy-adapter-dom.js',
|
|
19
|
+
'svelte-hmr'
|
|
20
|
+
];
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { log } from './log';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { createRequire } from 'module';
|
|
5
|
+
|
|
6
|
+
export function findRootSvelteDependencies(root: string, cwdFallback = true): SvelteDependency[] {
|
|
7
|
+
log.debug(`findSvelteDependencies: searching svelte dependencies in ${root}`);
|
|
8
|
+
const pkgFile = path.join(root, 'package.json');
|
|
9
|
+
if (!fs.existsSync(pkgFile)) {
|
|
10
|
+
if (cwdFallback) {
|
|
11
|
+
const cwd = process.cwd();
|
|
12
|
+
if (root !== cwd) {
|
|
13
|
+
log.debug(`no package.json found in vite root ${root}`);
|
|
14
|
+
return findRootSvelteDependencies(cwd, false);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
log.warn(`no package.json found, findRootSvelteDependencies failed`);
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const pkg = parsePkg(root);
|
|
22
|
+
if (!pkg) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const deps = [
|
|
27
|
+
...Object.keys(pkg.dependencies || {}),
|
|
28
|
+
...Object.keys(pkg.devDependencies || {})
|
|
29
|
+
].filter((dep) => !is_common_without_svelte_field(dep));
|
|
30
|
+
|
|
31
|
+
return getSvelteDependencies(deps, root);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function getSvelteDependencies(
|
|
35
|
+
deps: string[],
|
|
36
|
+
pkgDir: string,
|
|
37
|
+
path: string[] = []
|
|
38
|
+
): SvelteDependency[] {
|
|
39
|
+
const result = [];
|
|
40
|
+
const localRequire = createRequire(`${pkgDir}/package.json`);
|
|
41
|
+
const resolvedDeps = deps
|
|
42
|
+
.map((dep) => resolveDependencyData(dep, localRequire))
|
|
43
|
+
.filter(Boolean) as DependencyData[];
|
|
44
|
+
for (const { pkg, dir } of resolvedDeps) {
|
|
45
|
+
const type = getSvelteDependencyType(pkg);
|
|
46
|
+
if (!type) continue;
|
|
47
|
+
result.push({ name: pkg.name, type, pkg, dir, path });
|
|
48
|
+
// continue crawling for component libraries so we can optimize them, js libraries are fine
|
|
49
|
+
if (type === 'component-library' && pkg.dependencies) {
|
|
50
|
+
let dependencyNames = Object.keys(pkg.dependencies);
|
|
51
|
+
const circular = dependencyNames.filter((name) => path.includes(name));
|
|
52
|
+
if (circular.length > 0) {
|
|
53
|
+
log.warn.enabled &&
|
|
54
|
+
log.warn(
|
|
55
|
+
`skipping circular svelte dependencies in automated vite optimizeDeps handling`,
|
|
56
|
+
circular.map((x) => path.concat(x).join('>'))
|
|
57
|
+
);
|
|
58
|
+
dependencyNames = dependencyNames.filter((name) => !path.includes(name));
|
|
59
|
+
}
|
|
60
|
+
if (path.length === 3) {
|
|
61
|
+
log.debug.once(`encountered deep svelte dependency tree: ${path.join('>')}`);
|
|
62
|
+
}
|
|
63
|
+
result.push(...getSvelteDependencies(dependencyNames, dir, path.concat(pkg.name)));
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function resolveDependencyData(dep: string, localRequire: NodeRequire): DependencyData | void {
|
|
70
|
+
try {
|
|
71
|
+
const pkgJson = `${dep}/package.json`;
|
|
72
|
+
const pkg = localRequire(pkgJson);
|
|
73
|
+
const dir = path.dirname(localRequire.resolve(pkgJson));
|
|
74
|
+
return { dir, pkg };
|
|
75
|
+
} catch (e) {
|
|
76
|
+
log.debug.once(`dependency ${dep} does not export package.json`, e);
|
|
77
|
+
// walk up from default export until we find package.json with name=dep
|
|
78
|
+
try {
|
|
79
|
+
let dir = path.dirname(localRequire.resolve(dep));
|
|
80
|
+
while (dir) {
|
|
81
|
+
const pkg = parsePkg(dir, true);
|
|
82
|
+
if (pkg && pkg.name === dep) {
|
|
83
|
+
return { dir, pkg };
|
|
84
|
+
}
|
|
85
|
+
const parent = path.dirname(dir);
|
|
86
|
+
if (parent === dir) {
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
dir = parent;
|
|
90
|
+
}
|
|
91
|
+
} catch (e) {
|
|
92
|
+
log.debug.once(`error while trying to find package.json of ${dep}`, e);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
log.debug.once(`failed to resolve ${dep}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parsePkg(dir: string, silent = false): Pkg | void {
|
|
99
|
+
const pkgFile = path.join(dir, 'package.json');
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
|
|
102
|
+
} catch (e) {
|
|
103
|
+
!silent && log.warn.enabled && log.warn(`failed to parse ${pkgFile}`, e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function getSvelteDependencyType(pkg: Pkg): SvelteDependencyType | undefined {
|
|
108
|
+
if (isSvelteComponentLib(pkg)) {
|
|
109
|
+
return 'component-library';
|
|
110
|
+
} else if (isSvelteLib(pkg)) {
|
|
111
|
+
return 'js-library';
|
|
112
|
+
} else {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isSvelteComponentLib(pkg: Pkg) {
|
|
118
|
+
return !!pkg.svelte;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isSvelteLib(pkg: Pkg) {
|
|
122
|
+
return !!pkg.dependencies?.svelte || !!pkg.peerDependencies?.svelte;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const COMMON_DEPENDENCIES_WITHOUT_SVELTE_FIELD = [
|
|
126
|
+
'@lukeed/uuid',
|
|
127
|
+
'@sveltejs/vite-plugin-svelte',
|
|
128
|
+
'@sveltejs/kit',
|
|
129
|
+
'autoprefixer',
|
|
130
|
+
'cookie',
|
|
131
|
+
'dotenv',
|
|
132
|
+
'esbuild',
|
|
133
|
+
'eslint',
|
|
134
|
+
'jest',
|
|
135
|
+
'mdsvex',
|
|
136
|
+
'postcss',
|
|
137
|
+
'prettier',
|
|
138
|
+
'svelte',
|
|
139
|
+
'svelte-check',
|
|
140
|
+
'svelte-hmr',
|
|
141
|
+
'svelte-preprocess',
|
|
142
|
+
'tslib',
|
|
143
|
+
'typescript',
|
|
144
|
+
'vite'
|
|
145
|
+
];
|
|
146
|
+
const COMMON_PREFIXES_WITHOUT_SVELTE_FIELD = [
|
|
147
|
+
'@fontsource/',
|
|
148
|
+
'@postcss-plugins/',
|
|
149
|
+
'@rollup/',
|
|
150
|
+
'@sveltejs/adapter-',
|
|
151
|
+
'@types/',
|
|
152
|
+
'@typescript-eslint/',
|
|
153
|
+
'eslint-',
|
|
154
|
+
'jest-',
|
|
155
|
+
'postcss-plugin-',
|
|
156
|
+
'prettier-plugin-',
|
|
157
|
+
'rollup-plugin-',
|
|
158
|
+
'vite-plugin-'
|
|
159
|
+
];
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Test for common dependency names that tell us it is not a package including a svelte field, eg. eslint + plugins.
|
|
163
|
+
*
|
|
164
|
+
* This speeds up the find process as we don't have to try and require the package.json for all of them
|
|
165
|
+
*
|
|
166
|
+
* @param dependency {string}
|
|
167
|
+
* @returns {boolean} true if it is a dependency without a svelte field
|
|
168
|
+
*/
|
|
169
|
+
function is_common_without_svelte_field(dependency: string): boolean {
|
|
170
|
+
return (
|
|
171
|
+
COMMON_DEPENDENCIES_WITHOUT_SVELTE_FIELD.includes(dependency) ||
|
|
172
|
+
COMMON_PREFIXES_WITHOUT_SVELTE_FIELD.some(
|
|
173
|
+
(prefix) =>
|
|
174
|
+
prefix.startsWith('@')
|
|
175
|
+
? dependency.startsWith(prefix)
|
|
176
|
+
: dependency.substring(dependency.lastIndexOf('/') + 1).startsWith(prefix) // check prefix omitting @scope/
|
|
177
|
+
)
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function needsOptimization(dep: string, localRequire: NodeRequire): boolean {
|
|
182
|
+
const depData = resolveDependencyData(dep, localRequire);
|
|
183
|
+
if (!depData) return false;
|
|
184
|
+
const pkg = depData.pkg;
|
|
185
|
+
// only optimize if is cjs, using the below as heuristic
|
|
186
|
+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/162
|
|
187
|
+
const isCjs = pkg.main && !pkg.module && !pkg.exports;
|
|
188
|
+
if (!isCjs) return false;
|
|
189
|
+
// ensure entry is js so vite can prebundle it
|
|
190
|
+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/233
|
|
191
|
+
const entryExt = path.extname(pkg.main);
|
|
192
|
+
return !entryExt || entryExt === '.js' || entryExt === '.cjs';
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
interface DependencyData {
|
|
196
|
+
dir: string;
|
|
197
|
+
pkg: Pkg;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface SvelteDependency {
|
|
201
|
+
name: string;
|
|
202
|
+
type: SvelteDependencyType;
|
|
203
|
+
dir: string;
|
|
204
|
+
pkg: Pkg;
|
|
205
|
+
path: string[];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// component-library => exports svelte components
|
|
209
|
+
// js-library => only uses svelte api, no components
|
|
210
|
+
export type SvelteDependencyType = 'component-library' | 'js-library';
|
|
211
|
+
|
|
212
|
+
export interface Pkg {
|
|
213
|
+
name: string;
|
|
214
|
+
svelte?: string;
|
|
215
|
+
dependencies?: DependencyList;
|
|
216
|
+
devDependencies?: DependencyList;
|
|
217
|
+
peerDependencies?: DependencyList;
|
|
218
|
+
[key: string]: any;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export interface DependencyList {
|
|
222
|
+
[key: string]: string;
|
|
223
|
+
}
|