@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
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { RollupError } from 'rollup';
|
|
2
|
+
import { Warning } from './options';
|
|
3
|
+
import { buildExtendedLogMessage } from './log';
|
|
4
|
+
import { PartialMessage } from 'esbuild';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* convert an error thrown by svelte.compile to a RollupError so that vite displays it in a user friendly way
|
|
8
|
+
* @param error a svelte compiler error, which is a mix of Warning and an error
|
|
9
|
+
* @returns {RollupError} the converted error
|
|
10
|
+
*/
|
|
11
|
+
export function toRollupError(error: Warning & Error): RollupError {
|
|
12
|
+
const { filename, frame, start, code, name } = error;
|
|
13
|
+
const rollupError: RollupError = {
|
|
14
|
+
name, // needed otherwise sveltekit coalesce_to_error turns it into a string
|
|
15
|
+
id: filename,
|
|
16
|
+
message: buildExtendedLogMessage(error), // include filename:line:column so that it's clickable
|
|
17
|
+
frame: formatFrameForVite(frame),
|
|
18
|
+
code,
|
|
19
|
+
stack: ''
|
|
20
|
+
};
|
|
21
|
+
if (start) {
|
|
22
|
+
rollupError.loc = {
|
|
23
|
+
line: start.line,
|
|
24
|
+
column: start.column,
|
|
25
|
+
file: filename
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return rollupError;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* convert an error thrown by svelte.compile to an esbuild PartialMessage
|
|
33
|
+
* @param error a svelte compiler error, which is a mix of Warning and an error
|
|
34
|
+
* @returns {PartialMessage} the converted error
|
|
35
|
+
*/
|
|
36
|
+
export function toESBuildError(error: Warning & Error): PartialMessage {
|
|
37
|
+
const { filename, frame, start } = error;
|
|
38
|
+
const partialMessage: PartialMessage = {
|
|
39
|
+
text: buildExtendedLogMessage(error)
|
|
40
|
+
};
|
|
41
|
+
if (start) {
|
|
42
|
+
partialMessage.location = {
|
|
43
|
+
line: start.line,
|
|
44
|
+
column: start.column,
|
|
45
|
+
file: filename,
|
|
46
|
+
lineText: lineFromFrame(start.line, frame) // needed to get a meaningful error message on cli
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return partialMessage;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* extract line with number from codeframe
|
|
54
|
+
*/
|
|
55
|
+
function lineFromFrame(lineNo: number, frame?: string): string {
|
|
56
|
+
if (!frame) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
const lines = frame.split('\n');
|
|
60
|
+
const errorLine = lines.find((line) => line.trimStart().startsWith(`${lineNo}: `));
|
|
61
|
+
return errorLine ? errorLine.substring(errorLine.indexOf(': ') + 3) : '';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* vite error overlay expects a specific format to show frames
|
|
66
|
+
* this reformats svelte frame (colon separated, less whitespace)
|
|
67
|
+
* to one that vite displays on overlay ( pipe separated, more whitespace)
|
|
68
|
+
* e.g.
|
|
69
|
+
* ```
|
|
70
|
+
* 1: foo
|
|
71
|
+
* 2: bar;
|
|
72
|
+
* ^
|
|
73
|
+
* 3: baz
|
|
74
|
+
* ```
|
|
75
|
+
* to
|
|
76
|
+
* ```
|
|
77
|
+
* 1 | foo
|
|
78
|
+
* 2 | bar;
|
|
79
|
+
* ^
|
|
80
|
+
* 3 | baz
|
|
81
|
+
* ```
|
|
82
|
+
* @see https://github.com/vitejs/vite/blob/96591bf9989529de839ba89958755eafe4c445ae/packages/vite/src/client/overlay.ts#L116
|
|
83
|
+
*/
|
|
84
|
+
function formatFrameForVite(frame?: string): string {
|
|
85
|
+
if (!frame) {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
return frame
|
|
89
|
+
.split('\n')
|
|
90
|
+
.map((line) => (line.match(/^\s+\^/) ? ' ' + line : ' ' + line.replace(':', ' | ')))
|
|
91
|
+
.join('\n');
|
|
92
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import { compile, preprocess } from 'svelte/compiler';
|
|
3
|
+
import { DepOptimizationOptions } from 'vite';
|
|
4
|
+
import { Compiled } from './compile';
|
|
5
|
+
import { log } from './log';
|
|
6
|
+
import { CompileOptions, ResolvedOptions } from './options';
|
|
7
|
+
import { toESBuildError } from './error';
|
|
8
|
+
|
|
9
|
+
type EsbuildOptions = NonNullable<DepOptimizationOptions['esbuildOptions']>;
|
|
10
|
+
type EsbuildPlugin = NonNullable<EsbuildOptions['plugins']>[number];
|
|
11
|
+
type EsbuildPluginBuild = Parameters<EsbuildPlugin['setup']>[0];
|
|
12
|
+
|
|
13
|
+
export const facadeEsbuildSveltePluginName = 'vite-plugin-svelte:facade';
|
|
14
|
+
|
|
15
|
+
export function esbuildSveltePlugin(options: ResolvedOptions): EsbuildPlugin {
|
|
16
|
+
return {
|
|
17
|
+
name: 'vite-plugin-svelte:optimize-svelte',
|
|
18
|
+
setup(build) {
|
|
19
|
+
disableVitePrebundleSvelte(build);
|
|
20
|
+
|
|
21
|
+
const svelteExtensions = (options.extensions ?? ['.svelte']).map((ext) => ext.slice(1));
|
|
22
|
+
const svelteFilter = new RegExp(`\\.(` + svelteExtensions.join('|') + `)(\\?.*)?$`);
|
|
23
|
+
|
|
24
|
+
build.onLoad({ filter: svelteFilter }, async ({ path: filename }) => {
|
|
25
|
+
const code = await fs.readFile(filename, 'utf8');
|
|
26
|
+
try {
|
|
27
|
+
const contents = await compileSvelte(options, { filename, code });
|
|
28
|
+
return { contents };
|
|
29
|
+
} catch (e) {
|
|
30
|
+
return { errors: [toESBuildError(e)] };
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function disableVitePrebundleSvelte(build: EsbuildPluginBuild) {
|
|
38
|
+
const viteDepPrebundlePlugin = build.initialOptions.plugins?.find(
|
|
39
|
+
(v) => v.name === 'vite:dep-pre-bundle'
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (!viteDepPrebundlePlugin) return;
|
|
43
|
+
|
|
44
|
+
// Prevent vite:dep-pre-bundle from externalizing svelte files
|
|
45
|
+
const _setup = viteDepPrebundlePlugin.setup.bind(viteDepPrebundlePlugin);
|
|
46
|
+
viteDepPrebundlePlugin.setup = function (build) {
|
|
47
|
+
const _onResolve = build.onResolve.bind(build);
|
|
48
|
+
build.onResolve = function (options, callback) {
|
|
49
|
+
if (options.filter.source.includes('svelte')) {
|
|
50
|
+
options.filter = new RegExp(
|
|
51
|
+
options.filter.source.replace('|svelte', ''),
|
|
52
|
+
options.filter.flags
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
return _onResolve(options, callback);
|
|
56
|
+
};
|
|
57
|
+
return _setup(build);
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function compileSvelte(
|
|
62
|
+
options: ResolvedOptions,
|
|
63
|
+
{ filename, code }: { filename: string; code: string }
|
|
64
|
+
): Promise<string> {
|
|
65
|
+
const compileOptions: CompileOptions = {
|
|
66
|
+
...options.compilerOptions,
|
|
67
|
+
css: true,
|
|
68
|
+
filename,
|
|
69
|
+
format: 'esm',
|
|
70
|
+
generate: 'dom'
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let preprocessed;
|
|
74
|
+
|
|
75
|
+
if (options.preprocess) {
|
|
76
|
+
preprocessed = await preprocess(code, options.preprocess, { filename });
|
|
77
|
+
if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const finalCode = preprocessed ? preprocessed.code : code;
|
|
81
|
+
|
|
82
|
+
const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
|
|
83
|
+
filename,
|
|
84
|
+
code: finalCode,
|
|
85
|
+
compileOptions
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (dynamicCompileOptions && log.debug.enabled) {
|
|
89
|
+
log.debug(`dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const finalCompileOptions = dynamicCompileOptions
|
|
93
|
+
? {
|
|
94
|
+
...compileOptions,
|
|
95
|
+
...dynamicCompileOptions
|
|
96
|
+
}
|
|
97
|
+
: compileOptions;
|
|
98
|
+
|
|
99
|
+
const compiled = compile(finalCode, finalCompileOptions) as Compiled;
|
|
100
|
+
|
|
101
|
+
return compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl();
|
|
102
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
|
|
3
|
+
const hashes = Object.create(null);
|
|
4
|
+
|
|
5
|
+
//TODO shorter?
|
|
6
|
+
const hash_length = 12;
|
|
7
|
+
|
|
8
|
+
export function safeBase64Hash(input: string) {
|
|
9
|
+
if (hashes[input]) {
|
|
10
|
+
return hashes[input];
|
|
11
|
+
}
|
|
12
|
+
//TODO if performance really matters, use a faster one like xx-hash etc.
|
|
13
|
+
// should be evenly distributed because short input length and similarities in paths could cause collisions otherwise
|
|
14
|
+
// OR DON'T USE A HASH AT ALL, what about a simple counter?
|
|
15
|
+
const md5 = crypto.createHash('md5');
|
|
16
|
+
md5.update(input);
|
|
17
|
+
const hash = toSafe(md5.digest('base64')).substr(0, hash_length);
|
|
18
|
+
hashes[input] = hash;
|
|
19
|
+
return hash;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const replacements: { [key: string]: string } = {
|
|
23
|
+
'+': '-',
|
|
24
|
+
'/': '_',
|
|
25
|
+
'=': ''
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const replaceRE = new RegExp(`[${Object.keys(replacements).join('')}]`, 'g');
|
|
29
|
+
|
|
30
|
+
function toSafe(base64: string) {
|
|
31
|
+
return base64.replace(replaceRE, (x) => replacements[x]);
|
|
32
|
+
}
|
package/src/utils/id.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars */
|
|
2
|
+
import { createFilter } from '@rollup/pluginutils';
|
|
3
|
+
import { Arrayable, ResolvedOptions } from './options';
|
|
4
|
+
import { normalizePath } from 'vite';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
|
|
7
|
+
const VITE_FS_PREFIX = '/@fs/';
|
|
8
|
+
const IS_WINDOWS = process.platform === 'win32';
|
|
9
|
+
|
|
10
|
+
export type SvelteQueryTypes = 'style' | 'script';
|
|
11
|
+
|
|
12
|
+
export interface RequestQuery {
|
|
13
|
+
// our own
|
|
14
|
+
svelte?: boolean;
|
|
15
|
+
type?: SvelteQueryTypes;
|
|
16
|
+
// vite specific
|
|
17
|
+
url?: boolean;
|
|
18
|
+
raw?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface SvelteRequest {
|
|
22
|
+
id: string;
|
|
23
|
+
cssId: string;
|
|
24
|
+
filename: string;
|
|
25
|
+
normalizedFilename: string;
|
|
26
|
+
query: RequestQuery;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
ssr: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function splitId(id: string) {
|
|
32
|
+
const parts = id.split(`?`, 2);
|
|
33
|
+
const filename = parts[0];
|
|
34
|
+
const rawQuery = parts[1];
|
|
35
|
+
return { filename, rawQuery };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseToSvelteRequest(
|
|
39
|
+
id: string,
|
|
40
|
+
filename: string,
|
|
41
|
+
rawQuery: string,
|
|
42
|
+
root: string,
|
|
43
|
+
timestamp: number,
|
|
44
|
+
ssr: boolean
|
|
45
|
+
): SvelteRequest | undefined {
|
|
46
|
+
const query = parseRequestQuery(rawQuery);
|
|
47
|
+
if (query.url || query.raw) {
|
|
48
|
+
// skip requests with special vite tags
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const normalizedFilename = normalize(filename, root);
|
|
52
|
+
const cssId = createVirtualImportId(filename, root, 'style');
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
filename,
|
|
57
|
+
normalizedFilename,
|
|
58
|
+
cssId,
|
|
59
|
+
query,
|
|
60
|
+
timestamp,
|
|
61
|
+
ssr
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function createVirtualImportId(filename: string, root: string, type: SvelteQueryTypes) {
|
|
66
|
+
const parts = ['svelte', `type=${type}`];
|
|
67
|
+
if (type === 'style') {
|
|
68
|
+
parts.push('lang.css');
|
|
69
|
+
}
|
|
70
|
+
if (existsInRoot(filename, root)) {
|
|
71
|
+
filename = root + filename;
|
|
72
|
+
} else if (filename.startsWith(VITE_FS_PREFIX)) {
|
|
73
|
+
filename = IS_WINDOWS
|
|
74
|
+
? filename.slice(VITE_FS_PREFIX.length) // remove /@fs/ from /@fs/C:/...
|
|
75
|
+
: filename.slice(VITE_FS_PREFIX.length - 1); // remove /@fs from /@fs/home/user
|
|
76
|
+
}
|
|
77
|
+
// return same virtual id format as vite-plugin-vue eg ...App.svelte?svelte&type=style&lang.css
|
|
78
|
+
return `${filename}?${parts.join('&')}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function parseRequestQuery(rawQuery: string): RequestQuery {
|
|
82
|
+
const query = Object.fromEntries(new URLSearchParams(rawQuery));
|
|
83
|
+
for (const key in query) {
|
|
84
|
+
if (query[key] === '') {
|
|
85
|
+
// @ts-ignore
|
|
86
|
+
query[key] = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return query as RequestQuery;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* posixify and remove root at start
|
|
94
|
+
*
|
|
95
|
+
* @param filename
|
|
96
|
+
* @param normalizedRoot
|
|
97
|
+
*/
|
|
98
|
+
function normalize(filename: string, normalizedRoot: string) {
|
|
99
|
+
return stripRoot(normalizePath(filename), normalizedRoot);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function existsInRoot(filename: string, root: string) {
|
|
103
|
+
if (filename.startsWith(VITE_FS_PREFIX)) {
|
|
104
|
+
return false; // vite already tagged it as out of root
|
|
105
|
+
}
|
|
106
|
+
return fs.existsSync(root + filename);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function stripRoot(normalizedFilename: string, normalizedRoot: string) {
|
|
110
|
+
return normalizedFilename.startsWith(normalizedRoot + '/')
|
|
111
|
+
? normalizedFilename.slice(normalizedRoot.length)
|
|
112
|
+
: normalizedFilename;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function buildFilter(
|
|
116
|
+
include: Arrayable<string> | undefined,
|
|
117
|
+
exclude: Arrayable<string> | undefined,
|
|
118
|
+
extensions: string[]
|
|
119
|
+
): (filename: string) => boolean {
|
|
120
|
+
const rollupFilter = createFilter(include, exclude);
|
|
121
|
+
return (filename) => rollupFilter(filename) && extensions.some((ext) => filename.endsWith(ext));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export type IdParser = (id: string, ssr: boolean, timestamp?: number) => SvelteRequest | undefined;
|
|
125
|
+
export function buildIdParser(options: ResolvedOptions): IdParser {
|
|
126
|
+
const { include, exclude, extensions, root } = options;
|
|
127
|
+
const normalizedRoot = normalizePath(root);
|
|
128
|
+
const filter = buildFilter(include, exclude, extensions!);
|
|
129
|
+
return (id, ssr, timestamp = Date.now()) => {
|
|
130
|
+
const { filename, rawQuery } = splitId(id);
|
|
131
|
+
if (filter(filename)) {
|
|
132
|
+
return parseToSvelteRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { createRequire } from 'module';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import { pathToFileURL } from 'url';
|
|
5
|
+
import { log } from './log';
|
|
6
|
+
import { Options } from './options';
|
|
7
|
+
import { UserConfig } from 'vite';
|
|
8
|
+
|
|
9
|
+
// used to require cjs config in esm.
|
|
10
|
+
// NOTE dynamic import() cjs technically works, but timestamp query cache bust
|
|
11
|
+
// have no effect, likely because it has another internal cache?
|
|
12
|
+
let esmRequire: NodeRequire;
|
|
13
|
+
|
|
14
|
+
export const knownSvelteConfigNames = [
|
|
15
|
+
'svelte.config.js',
|
|
16
|
+
'svelte.config.cjs',
|
|
17
|
+
'svelte.config.mjs'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
// hide dynamic import from ts transform to prevent it turning into a require
|
|
21
|
+
// see https://github.com/microsoft/TypeScript/issues/43329#issuecomment-811606238
|
|
22
|
+
// also use timestamp query to avoid caching on reload
|
|
23
|
+
const dynamicImportDefault = new Function(
|
|
24
|
+
'path',
|
|
25
|
+
'return import(path + "?t=" + Date.now()).then(m => m.default)'
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export async function loadSvelteConfig(
|
|
29
|
+
viteConfig: UserConfig,
|
|
30
|
+
inlineOptions: Partial<Options>
|
|
31
|
+
): Promise<Partial<Options> | undefined> {
|
|
32
|
+
const configFile = findConfigToLoad(viteConfig, inlineOptions);
|
|
33
|
+
if (configFile) {
|
|
34
|
+
let err;
|
|
35
|
+
// try to use dynamic import for svelte.config.js first
|
|
36
|
+
if (configFile.endsWith('.js') || configFile.endsWith('.mjs')) {
|
|
37
|
+
try {
|
|
38
|
+
const result = await dynamicImportDefault(pathToFileURL(configFile).href);
|
|
39
|
+
if (result != null) {
|
|
40
|
+
return {
|
|
41
|
+
...result,
|
|
42
|
+
configFile
|
|
43
|
+
};
|
|
44
|
+
} else {
|
|
45
|
+
throw new Error(`invalid export in ${configFile}`);
|
|
46
|
+
}
|
|
47
|
+
} catch (e) {
|
|
48
|
+
log.error(`failed to import config ${configFile}`, e);
|
|
49
|
+
err = e;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// cjs or error with dynamic import
|
|
53
|
+
if (!configFile.endsWith('.mjs')) {
|
|
54
|
+
try {
|
|
55
|
+
// identify which require function to use (esm and cjs mode)
|
|
56
|
+
const _require = import.meta.url
|
|
57
|
+
? (esmRequire ??= createRequire(import.meta.url))
|
|
58
|
+
: require;
|
|
59
|
+
|
|
60
|
+
// avoid loading cached version on reload
|
|
61
|
+
delete _require.cache[_require.resolve(configFile)];
|
|
62
|
+
const result = _require(configFile);
|
|
63
|
+
if (result != null) {
|
|
64
|
+
return {
|
|
65
|
+
...result,
|
|
66
|
+
configFile
|
|
67
|
+
};
|
|
68
|
+
} else {
|
|
69
|
+
throw new Error(`invalid export in ${configFile}`);
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
log.error(`failed to require config ${configFile}`, e);
|
|
73
|
+
if (!err) {
|
|
74
|
+
err = e;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// failed to load existing config file
|
|
79
|
+
throw err;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function findConfigToLoad(viteConfig: UserConfig, inlineOptions: Partial<Options>) {
|
|
84
|
+
const root = viteConfig.root || process.cwd();
|
|
85
|
+
if (inlineOptions.configFile) {
|
|
86
|
+
const abolutePath = path.isAbsolute(inlineOptions.configFile)
|
|
87
|
+
? inlineOptions.configFile
|
|
88
|
+
: path.resolve(root, inlineOptions.configFile);
|
|
89
|
+
if (!fs.existsSync(abolutePath)) {
|
|
90
|
+
throw new Error(`failed to find svelte config file ${abolutePath}.`);
|
|
91
|
+
}
|
|
92
|
+
return abolutePath;
|
|
93
|
+
} else {
|
|
94
|
+
const existingKnownConfigFiles = knownSvelteConfigNames
|
|
95
|
+
.map((candidate) => path.resolve(root, candidate))
|
|
96
|
+
.filter((file) => fs.existsSync(file));
|
|
97
|
+
if (existingKnownConfigFiles.length === 0) {
|
|
98
|
+
log.debug(`no svelte config found at ${root}`);
|
|
99
|
+
return;
|
|
100
|
+
} else if (existingKnownConfigFiles.length > 1) {
|
|
101
|
+
log.warn(
|
|
102
|
+
`found more than one svelte config file, using ${existingKnownConfigFiles[0]}. you should only have one!`,
|
|
103
|
+
existingKnownConfigFiles
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
return existingKnownConfigFiles[0];
|
|
107
|
+
}
|
|
108
|
+
}
|
package/src/utils/log.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/* eslint-disable no-unused-vars,no-console */
|
|
2
|
+
import { cyan, yellow, red } from 'kleur/colors';
|
|
3
|
+
import debug from 'debug';
|
|
4
|
+
import { ResolvedOptions, Warning } from './options';
|
|
5
|
+
|
|
6
|
+
const levels: string[] = ['debug', 'info', 'warn', 'error', 'silent'];
|
|
7
|
+
const prefix = 'vite-plugin-svelte';
|
|
8
|
+
const loggers: { [key: string]: any } = {
|
|
9
|
+
debug: {
|
|
10
|
+
log: debug(`vite:${prefix}`),
|
|
11
|
+
enabled: false,
|
|
12
|
+
isDebug: true
|
|
13
|
+
},
|
|
14
|
+
info: {
|
|
15
|
+
color: cyan,
|
|
16
|
+
log: console.log,
|
|
17
|
+
enabled: true
|
|
18
|
+
},
|
|
19
|
+
warn: {
|
|
20
|
+
color: yellow,
|
|
21
|
+
log: console.warn,
|
|
22
|
+
enabled: true
|
|
23
|
+
},
|
|
24
|
+
error: {
|
|
25
|
+
color: red,
|
|
26
|
+
log: console.error,
|
|
27
|
+
enabled: true
|
|
28
|
+
},
|
|
29
|
+
silent: {
|
|
30
|
+
enabled: false
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let _level: string = 'info';
|
|
35
|
+
function setLevel(level: string) {
|
|
36
|
+
if (level === _level) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const levelIndex = levels.indexOf(level);
|
|
40
|
+
if (levelIndex > -1) {
|
|
41
|
+
_level = level;
|
|
42
|
+
for (let i = 0; i < levels.length; i++) {
|
|
43
|
+
loggers[levels[i]].enabled = i >= levelIndex;
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
_log(loggers.error, `invalid log level: ${level} `);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function _log(logger: any, message: string, payload?: any) {
|
|
51
|
+
if (!logger.enabled) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (logger.isDebug) {
|
|
55
|
+
payload !== undefined ? logger.log(message, payload) : logger.log(message);
|
|
56
|
+
} else {
|
|
57
|
+
logger.log(logger.color(`${new Date().toLocaleTimeString()} [${prefix}] ${message}`));
|
|
58
|
+
if (payload) {
|
|
59
|
+
logger.log(payload);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface LogFn {
|
|
65
|
+
(message: string, payload?: any): void;
|
|
66
|
+
enabled: boolean;
|
|
67
|
+
once: (message: string, payload?: any) => void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createLogger(level: string): LogFn {
|
|
71
|
+
const logger = loggers[level];
|
|
72
|
+
const logFn: LogFn = _log.bind(null, logger) as LogFn;
|
|
73
|
+
const logged = new Set<String>();
|
|
74
|
+
const once = function (message: string, payload?: any) {
|
|
75
|
+
if (logged.has(message)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
logged.add(message);
|
|
79
|
+
logFn.apply(null, [message, payload]);
|
|
80
|
+
};
|
|
81
|
+
Object.defineProperty(logFn, 'enabled', {
|
|
82
|
+
get() {
|
|
83
|
+
return logger.enabled;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
Object.defineProperty(logFn, 'once', {
|
|
87
|
+
get() {
|
|
88
|
+
return once;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return logFn;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const log = {
|
|
95
|
+
debug: createLogger('debug'),
|
|
96
|
+
info: createLogger('info'),
|
|
97
|
+
warn: createLogger('warn'),
|
|
98
|
+
error: createLogger('error'),
|
|
99
|
+
setLevel
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export function logCompilerWarnings(warnings: Warning[], options: ResolvedOptions) {
|
|
103
|
+
const { emitCss, onwarn, isBuild } = options;
|
|
104
|
+
const warn = isBuild ? warnBuild : warnDev;
|
|
105
|
+
const notIgnoredWarnings = warnings?.filter((w) => !ignoreCompilerWarning(w, isBuild, emitCss));
|
|
106
|
+
const extraWarnings = buildExtraWarnings(warnings, isBuild);
|
|
107
|
+
[...notIgnoredWarnings, ...extraWarnings].forEach((warning) => {
|
|
108
|
+
if (onwarn) {
|
|
109
|
+
onwarn(warning, warn);
|
|
110
|
+
} else {
|
|
111
|
+
warn(warning);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function ignoreCompilerWarning(
|
|
117
|
+
warning: Warning,
|
|
118
|
+
isBuild: boolean,
|
|
119
|
+
emitCss: boolean | undefined
|
|
120
|
+
): boolean {
|
|
121
|
+
return (
|
|
122
|
+
(!emitCss && warning.code === 'css-unused-selector') || // same as rollup-plugin-svelte
|
|
123
|
+
(!isBuild && isNoScopableElementWarning(warning))
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isNoScopableElementWarning(warning: Warning) {
|
|
128
|
+
// see https://github.com/sveltejs/vite-plugin-svelte/issues/153
|
|
129
|
+
return warning.code === 'css-unused-selector' && warning.message.includes('"*"');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function buildExtraWarnings(warnings: Warning[], isBuild: boolean): Warning[] {
|
|
133
|
+
const extraWarnings = [];
|
|
134
|
+
if (!isBuild) {
|
|
135
|
+
const noScopableElementWarnings = warnings.filter((w) => isNoScopableElementWarning(w));
|
|
136
|
+
if (noScopableElementWarnings.length > 0) {
|
|
137
|
+
// in case there are multiple, use last one as that is the one caused by our *{} rule
|
|
138
|
+
const noScopableElementWarning =
|
|
139
|
+
noScopableElementWarnings[noScopableElementWarnings.length - 1];
|
|
140
|
+
extraWarnings.push({
|
|
141
|
+
...noScopableElementWarning,
|
|
142
|
+
code: 'vite-plugin-svelte-css-no-scopable-elements',
|
|
143
|
+
message: `No scopable elements found in template. If you're using global styles in the style tag, you should move it into an external stylesheet file and import it in JS. See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#where-should-i-put-my-global-styles.`
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return extraWarnings;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function warnDev(w: Warning) {
|
|
151
|
+
log.info.enabled && log.info(buildExtendedLogMessage(w));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function warnBuild(w: Warning) {
|
|
155
|
+
log.warn.enabled && log.warn(buildExtendedLogMessage(w), w.frame);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function buildExtendedLogMessage(w: Warning) {
|
|
159
|
+
const parts = [];
|
|
160
|
+
if (w.filename) {
|
|
161
|
+
parts.push(w.filename);
|
|
162
|
+
}
|
|
163
|
+
if (w.start) {
|
|
164
|
+
parts.push(':', w.start.line, ':', w.start.column);
|
|
165
|
+
}
|
|
166
|
+
if (w.message) {
|
|
167
|
+
parts.push(' ', w.message);
|
|
168
|
+
}
|
|
169
|
+
return parts.join('');
|
|
170
|
+
}
|