File size: 6,031 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
import { buildExtendedLogMessage } from './log.js';
/**
* convert an error thrown by svelte.compile to a RollupError so that vite displays it in a user friendly way
* @param {import('svelte/types/compiler/interfaces').Warning & Error} error a svelte compiler error, which is a mix of Warning and an error
* @param {import('../types/options.d.ts').ResolvedOptions} options
* @returns {import('vite').Rollup.RollupError} the converted error
*/
export function toRollupError(error, options) {
const { filename, frame, start, code, name, stack } = error;
/** @type {import('vite').Rollup.RollupError} */
const rollupError = {
name, // needed otherwise sveltekit coalesce_to_error turns it into a string
id: filename,
message: buildExtendedLogMessage(error), // include filename:line:column so that it's clickable
frame: formatFrameForVite(frame),
code,
stack: options.isBuild || options.isDebug || !frame ? stack : ''
};
if (start) {
rollupError.loc = {
line: start.line,
column: start.column,
file: filename
};
}
return rollupError;
}
/**
* convert an error thrown by svelte.compile to an esbuild PartialMessage
* @param {import('svelte/types/compiler/interfaces').Warning & Error} error a svelte compiler error, which is a mix of Warning and an error
* @param {import('../types/options.d.ts').ResolvedOptions} options
* @returns {import('esbuild').PartialMessage} the converted error
*/
export function toESBuildError(error, options) {
const { filename, frame, start, stack } = error;
/** @type {import('esbuild').PartialMessage} */
const partialMessage = {
text: buildExtendedLogMessage(error)
};
if (start) {
partialMessage.location = {
line: start.line,
column: start.column,
file: filename,
lineText: lineFromFrame(start.line, frame) // needed to get a meaningful error message on cli
};
}
if (options.isBuild || options.isDebug || !frame) {
partialMessage.detail = stack;
}
return partialMessage;
}
/**
* extract line with number from codeframe
*
* @param {number} lineNo
* @param {string} [frame]
* @returns {string}
*/
function lineFromFrame(lineNo, frame) {
if (!frame) {
return '';
}
const lines = frame.split('\n');
const errorLine = lines.find((line) => line.trimStart().startsWith(`${lineNo}: `));
return errorLine ? errorLine.substring(errorLine.indexOf(': ') + 3) : '';
}
/**
* vite error overlay expects a specific format to show frames
* this reformats svelte frame (colon separated, less whitespace)
* to one that vite displays on overlay ( pipe separated, more whitespace)
* e.g.
* ```
* 1: foo
* 2: bar;
* ^
* 3: baz
* ```
* to
* ```
* 1 | foo
* 2 | bar;
* ^
* 3 | baz
* ```
* @see https://github.com/vitejs/vite/blob/96591bf9989529de839ba89958755eafe4c445ae/packages/vite/src/client/overlay.ts#L116
* @param {string} [frame]
* @returns {string}
*/
function formatFrameForVite(frame) {
if (!frame) {
return '';
}
return frame
.split('\n')
.map((line) => (line.match(/^\s+\^/) ? ' ' + line : ' ' + line.replace(':', ' | ')))
.join('\n');
}
/**
* @param {import('svelte/types/compiler/interfaces').Warning & Error} err a svelte compiler error, which is a mix of Warning and an error
* @param {string} originalCode
* @param {import('../public.d.ts').Options['preprocess']} [preprocessors]
*/
export function enhanceCompileError(err, originalCode, preprocessors) {
preprocessors = arraify(preprocessors ?? []);
/** @type {string[]} */
const additionalMessages = [];
// Handle incorrect TypeScript usage
if (err.code === 'parse-error') {
// Reference from Svelte: https://github.com/sveltejs/svelte/blob/9926347ad9dbdd0f3324d5538e25dcb7f5e442f8/packages/svelte/src/compiler/preprocess/index.js#L259
const scriptRe =
/<!--[^]*?-->|<script((?:\s+[^=>'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/]+)*\s*)(?:\/>|>([\S\s]*?)<\/script>)/g;
const errIndex = err.pos ?? -1;
let m;
while ((m = scriptRe.exec(originalCode))) {
const matchStart = m.index;
const matchEnd = matchStart + m[0].length;
const isErrorInScript = matchStart <= errIndex && errIndex <= matchEnd;
if (isErrorInScript) {
// Warn missing lang="ts"
const hasLangTs = m[1]?.includes('lang="ts"');
if (!hasLangTs) {
additionalMessages.push('Did you forget to add lang="ts" to your script tag?');
}
// Warn missing script preprocessor
if (preprocessors.every((p) => p.script == null)) {
const preprocessorType = hasLangTs ? 'TypeScript' : 'script';
additionalMessages.push(
`Did you forget to add a ${preprocessorType} preprocessor? See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/preprocess.md for more information.`
);
}
}
}
}
// Handle incorrect CSS preprocessor usage
if (err.code === 'css-syntax-error') {
// Reference from Svelte: https://github.com/sveltejs/svelte/blob/9926347ad9dbdd0f3324d5538e25dcb7f5e442f8/packages/svelte/src/compiler/preprocess/index.js#L257
const styleRe =
/<!--[^]*?-->|<style((?:\s+[^=>'"/]+=(?:"[^"]*"|'[^']*'|[^>\s]+)|\s+[^=>'"/]+)*\s*)(?:\/>|>([\S\s]*?)<\/style>)/g;
let m;
while ((m = styleRe.exec(originalCode))) {
// Warn missing lang attribute
if (!m[1]?.includes('lang=')) {
additionalMessages.push('Did you forget to add a lang attribute to your style tag?');
}
// Warn missing style preprocessor
if (
preprocessors.every((p) => p.style == null || p.name === 'inject-scope-everything-rule')
) {
const preprocessorType = m[1]?.match(/lang="(.+?)"/)?.[1] ?? 'style';
additionalMessages.push(
`Did you forget to add a ${preprocessorType} preprocessor? See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/preprocess.md for more information.`
);
}
}
}
if (additionalMessages.length) {
err.message += '\n\n- ' + additionalMessages.join('\n- ');
}
return err;
}
/**
* @param {T | T[]} value
* @template T
*/
function arraify(value) {
return Array.isArray(value) ? value : [value];
}
|