@gracile-labs/better-errors 0.1.0-next.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.
@@ -0,0 +1,271 @@
1
+ /* eslint-disable */
2
+ import c from 'picocolors';
3
+ import * as fs from 'node:fs';
4
+ import { isAbsolute, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { normalizePath } from 'vite';
7
+ import { codeFrame } from './printer.js';
8
+ import { removeLeadingForwardSlashWindows } from '@gracile/internal-utils/paths';
9
+ import { AggregateError, } from '../errors.js';
10
+ const ansiPattern = [
11
+ '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)',
12
+ '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))',
13
+ ].join('|');
14
+ const stripAnsi = (input, { onlyFirst = false } = {}) => {
15
+ return input.replace(new RegExp(ansiPattern, onlyFirst ? undefined : 'g'), '');
16
+ };
17
+ /** Coalesce any throw variable to an Error instance. */
18
+ export function createSafeError(err) {
19
+ if (err instanceof Error || (err?.name && err.message)) {
20
+ return err;
21
+ }
22
+ else {
23
+ const error = new Error(JSON.stringify(err));
24
+ error['hint'] =
25
+ `To get as much information as possible from your errors, make sure to throw Error objects instead of \`${typeof err}\`. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error for more information.`;
26
+ return error;
27
+ }
28
+ }
29
+ export function normalizeLF(code) {
30
+ return code.replace(/\r\n|\r(?!\n)|\n/g, '\n');
31
+ }
32
+ /**
33
+ * Takes any error-like object and returns a standardized Error + metadata object.
34
+ * Useful for consistent reporting regardless of where the error surfaced from.
35
+ */
36
+ export function collectErrorMetadata(e, rootFolder) {
37
+ const err = AggregateError.is(e) || Array.isArray(e.errors)
38
+ ? e.errors
39
+ : [e];
40
+ err.forEach((error) => {
41
+ if (e.stack) {
42
+ const stackInfo = collectInfoFromStacktrace(e);
43
+ try {
44
+ error.stack = stripAnsi(stackInfo.stack);
45
+ }
46
+ catch { }
47
+ if (stackInfo.loc)
48
+ error.loc = stackInfo.loc;
49
+ if (stackInfo.plugin)
50
+ error.plugin = stackInfo.plugin;
51
+ if (stackInfo.pluginCode)
52
+ error.pluginCode = stackInfo.pluginCode;
53
+ }
54
+ // Make sure the file location is absolute, otherwise:
55
+ // - It won't be clickable in the terminal
56
+ // - We'll fail to show the file's content in the browser
57
+ // - We'll fail to show the code frame in the terminal
58
+ // - The "Open in Editor" button won't work
59
+ // Normalize the paths so that we can correctly detect if it's absolute on any platform
60
+ const normalizedFile = normalizePath(error.loc?.file || '');
61
+ const normalizedRootFolder = removeLeadingForwardSlashWindows(rootFolder?.pathname || '');
62
+ if (error.loc?.file &&
63
+ rootFolder &&
64
+ (!normalizedFile?.startsWith(normalizedRootFolder) ||
65
+ !isAbsolute(normalizedFile))) {
66
+ error.loc.file = join(fileURLToPath(rootFolder), error.loc.file);
67
+ }
68
+ // If we don't have a frame, but we have a location let's try making up a frame for it
69
+ if (error.loc && (!error.frame || !error['fullCode'])) {
70
+ try {
71
+ const fileContents = fs.readFileSync(error.loc.file, 'utf8');
72
+ if (!error.frame) {
73
+ const frame = codeFrame(fileContents, error.loc);
74
+ error.frame = stripAnsi(frame);
75
+ }
76
+ if (!error['fullCode']) {
77
+ error['fullCode'] = fileContents;
78
+ }
79
+ }
80
+ catch { }
81
+ }
82
+ // Generic error (probably from Vite, and already formatted)
83
+ error['hint'] = generateHint(e);
84
+ // Strip ANSI for `message` property. Note that ESBuild errors may not have the property,
85
+ // but it will be handled and added below, which is already ANSI-free
86
+ if (error.message) {
87
+ try {
88
+ error.message = stripAnsi(error.message);
89
+ }
90
+ catch {
91
+ // Setting `error.message` can fail here if the message is read-only, which for the vast majority of cases will never happen, however some somewhat obscure cases can cause this to happen.
92
+ }
93
+ }
94
+ });
95
+ // If we received an array of errors and it's not from us, it's most likely from ESBuild, try to extract info for Vite to display
96
+ // NOTE: We still need to be defensive here, because it might not necessarily be from ESBuild, it's just fairly likely.
97
+ if (!AggregateError.is(e) && Array.isArray(e.errors)) {
98
+ e.errors.forEach((buildError, i) => {
99
+ const { location, pluginName, text } = buildError;
100
+ if (!err[i])
101
+ return;
102
+ // ESBuild can give us a slightly better error message than the one in the error, so let's use it
103
+ if (text) {
104
+ try {
105
+ err[i].message = text;
106
+ }
107
+ catch { }
108
+ }
109
+ if (location) {
110
+ err[i].loc = {
111
+ file: location.file,
112
+ line: location.line,
113
+ column: location.column,
114
+ };
115
+ err[i].id = err[0]?.id || location?.file;
116
+ }
117
+ // Vite adds the error message to the frame for ESBuild errors, we don't want that
118
+ if (err[i].frame) {
119
+ const errorLines = err[i].frame?.trim().split('\n');
120
+ if (errorLines && errorLines[0]) {
121
+ err[i].frame = !/^\d/.test(errorLines[0])
122
+ ? errorLines?.slice(1).join('\n')
123
+ : err[i].frame;
124
+ }
125
+ }
126
+ const possibleFilePath = location?.file ?? err[i].id;
127
+ if (possibleFilePath &&
128
+ err[i].loc &&
129
+ (!err[i].frame || !err[i]['fullCode'])) {
130
+ try {
131
+ const fileContents = fs.readFileSync(possibleFilePath, 'utf8');
132
+ if (!err[i].frame) {
133
+ err[i].frame = codeFrame(fileContents, {
134
+ ...err[i].loc,
135
+ file: possibleFilePath,
136
+ });
137
+ }
138
+ err[i]['fullCode'] = fileContents;
139
+ }
140
+ catch {
141
+ err[i]['fullCode'] = err[i].pluginCode;
142
+ }
143
+ }
144
+ if (pluginName) {
145
+ err[i].plugin = pluginName;
146
+ }
147
+ if (err[0])
148
+ err[i]['hint'] = generateHint(err[0]);
149
+ });
150
+ }
151
+ // TODO: Handle returning multiple errors
152
+ // if (err[0]) return err[0];
153
+ return err[0];
154
+ }
155
+ function generateHint(err) {
156
+ const commonBrowserAPIs = ['document', 'window'];
157
+ // if (
158
+ // /Unknown file extension "\.(?:jsx|vue|svelte|astro|css)" for /.test(
159
+ // err.message,
160
+ // )
161
+ // ) {
162
+ // return 'You likely need to add this package to `vite.ssr.noExternal` in your astro config file.';
163
+ // } else if (commonBrowserAPIs.some((api) => err.toString().includes(api))) {
164
+ // const hint = `Browser APIs are not available on the server.
165
+ // ${
166
+ // err.loc?.file?.endsWith('.astro')
167
+ // ? 'Move your code to a <script> tag outside of the frontmatter, so the code runs on the client.'
168
+ // : 'If the code is in a framework component, try to access these objects after rendering using lifecycle methods or use a `client:only` directive to make the component exclusively run on the client.'
169
+ // }
170
+ // See https://docs.astro.build/en/guides/troubleshooting/#document-or-window-is-not-defined for more information.
171
+ // `;
172
+ // return hint;
173
+ // }
174
+ return err.hint;
175
+ }
176
+ function collectInfoFromStacktrace(error) {
177
+ // @ts-expect-error exact optional
178
+ let stackInfo = {
179
+ stack: error.stack,
180
+ plugin: error.plugin,
181
+ pluginCode: error.pluginCode,
182
+ loc: error.loc,
183
+ };
184
+ // normalize error stack line-endings to \n
185
+ stackInfo.stack = normalizeLF(error.stack);
186
+ const stackText = stripAnsi(error.stack);
187
+ // Try to find possible location from stack if we don't have one
188
+ if (!stackInfo.loc || (!stackInfo.loc.column && !stackInfo.loc.line)) {
189
+ const possibleFilePath = error.loc?.file ||
190
+ error.pluginCode ||
191
+ error.id ||
192
+ // TODO: this could be better, `src` might be something else
193
+ stackText
194
+ .split('\n')
195
+ .find((ln) => ln.includes('src') || ln.includes('node_modules'));
196
+ // Disable eslint as we're not sure how to improve this regex yet
197
+ // eslint-disable-next-line regexp/no-super-linear-backtracking
198
+ const source = possibleFilePath
199
+ ?.replace?.(/^[^(]+\(([^)]+).*$/, '$1')
200
+ .replace(/^\s+at\s+/, '');
201
+ let file = source?.replace(/:\d+/g, '');
202
+ const location = /:(\d+):(\d+)/.exec(source) ?? [];
203
+ const line = location[1];
204
+ const column = location[2];
205
+ if (file && line && column) {
206
+ try {
207
+ file = fileURLToPath(file);
208
+ }
209
+ catch { }
210
+ stackInfo.loc = {
211
+ file,
212
+ line: Number.parseInt(line),
213
+ column: Number.parseInt(column),
214
+ };
215
+ }
216
+ }
217
+ // // Derive plugin from stack (if possible)
218
+ // if (!stackInfo.plugin) {
219
+ // const stack =
220
+ // /withastro\/astro\/packages\/integrations\/([\w-]+)/i
221
+ // .exec(stackText)
222
+ // ?.at(1) ||
223
+ // /(@astrojs\/[\w-]+)\/(server|client|index)/i.exec(stackText)?.at(1);
224
+ // if (stack) stackInfo.plugin = stack;
225
+ // }
226
+ // Normalize stack (remove `/@fs/` urls, etc)
227
+ stackInfo.stack = cleanErrorStack(error.stack);
228
+ return stackInfo;
229
+ }
230
+ function cleanErrorStack(stack) {
231
+ return stack
232
+ .split(/\n/)
233
+ .map((l) => l.replace(/\/@fs\//g, '/'))
234
+ .join('\n');
235
+ }
236
+ export function getDocsForError(err, errorsData, docsBaseUrl) {
237
+ if (err.name !== 'UnknownError' && err.name in errorsData) {
238
+ return `${docsBaseUrl}/${getKebabErrorName(err.name)}/`;
239
+ }
240
+ return undefined;
241
+ /**
242
+ * The docs has kebab-case urls for errors, so we need to convert the error name
243
+ * @param errorName
244
+ */
245
+ function getKebabErrorName(errorName) {
246
+ return errorName.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
247
+ }
248
+ }
249
+ const linkRegex = /\[([^[]+)\]\((.*)\)/g;
250
+ const boldRegex = /\*\*(.+)\*\*/g;
251
+ const urlRegex = / ((?:https?|ftp):\/\/[-\w+&@#\\/%?=~|!:,.;]*[-\w+&@#\\/%=~|])/gi;
252
+ const codeRegex = /`([^`]+)`/g;
253
+ /**
254
+ * Render a subset of Markdown to HTML or a CLI output
255
+ */
256
+ export function renderErrorMarkdown(markdown, target) {
257
+ if (target === 'html') {
258
+ // escape()
259
+ return markdown
260
+ .replace(linkRegex, `<a href="$2" target="_blank">$1</a>`)
261
+ .replace(boldRegex, '<b>$1</b>')
262
+ .replace(urlRegex, ' <a href="$1" target="_blank">$1</a>')
263
+ .replace(codeRegex, '<code>$1</code>');
264
+ }
265
+ else {
266
+ return markdown
267
+ .replace(linkRegex, (_, m1, m2) => `${c.bold(m1)} ${c.underline(m2)}`)
268
+ .replace(urlRegex, (fullMatch) => ` ${c.underline(fullMatch.trim())}`)
269
+ .replace(boldRegex, (_, m1) => `${c.bold(m1)}`);
270
+ }
271
+ }
@@ -0,0 +1,33 @@
1
+ import type { BetterErrorData } from './../errors-data.js';
2
+ import type { ErrorPayload, ViteDevServer } from 'vite';
3
+ import type { ErrorWithMetadata } from '../errors.js';
4
+ export declare function enhanceViteSSRError({ error, filePath, vite, }: {
5
+ error: unknown;
6
+ filePath?: URL | undefined;
7
+ vite?: ViteDevServer | undefined;
8
+ }): Error;
9
+ export interface BetterErrorPayload {
10
+ type: ErrorPayload['type'];
11
+ err: Omit<ErrorPayload['err'], 'loc'> & {
12
+ name?: string;
13
+ title?: string;
14
+ hint?: string;
15
+ docslink?: string;
16
+ highlightedCode?: string;
17
+ loc?: {
18
+ file?: string;
19
+ line?: number;
20
+ column?: number;
21
+ };
22
+ cause?: unknown;
23
+ };
24
+ }
25
+ /**
26
+ * Generate a payload for Vite's error overlay
27
+ */
28
+ export declare function getViteErrorPayload({ err, errorsData, docsBaseUrl, }: {
29
+ err: ErrorWithMetadata;
30
+ errorsData?: Record<string, BetterErrorData>;
31
+ docsBaseUrl: string;
32
+ }): Promise<BetterErrorPayload>;
33
+ //# sourceMappingURL=vite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../../src/dev/vite.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,MAAM,CAAC;AASxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,wBAAgB,mBAAmB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,IAAI,GAEJ,EAAE;IACF,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,GAAG,GAAG,SAAS,CAAC;IAC3B,IAAI,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;CAEjC,GAAG,KAAK,CAwFR;AAED,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IAC3B,GAAG,EAAE,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG;QACvC,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,GAAG,CAAC,EAAE;YACL,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,MAAM,CAAC,EAAE,MAAM,CAAC;SAChB,CAAC;QACF,KAAK,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACF;AAcD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,EACzC,GAAG,EACH,UAAU,EACV,WAAW,GACX,EAAE;IACF,GAAG,EAAE,iBAAiB,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAkF9B"}
@@ -0,0 +1,191 @@
1
+ /* eslint-disable */
2
+ import * as fs from 'node:fs';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { getDocsForError, createSafeError, renderErrorMarkdown, } from './utils.js';
5
+ import { html, render } from '@lit-labs/ssr';
6
+ import { collectResultSync } from '@lit-labs/ssr/lib/render-result.js';
7
+ import { FailedToLoadModuleSSR } from '../errors-data.js';
8
+ export function enhanceViteSSRError({ error, filePath, vite,
9
+ // renderers,
10
+ }) {
11
+ // NOTE: We don't know where the error that's coming here comes from, so we need to be defensive regarding what we do
12
+ // to it to make sure we keep as much information as possible. It's very possible that we receive an error that does not
13
+ // follow any kind of standard formats (ex: a number, a string etc)
14
+ // let safeError = error as ErrorWithMetadata;
15
+ let safeError = createSafeError(error);
16
+ // Vite will give you better stacktraces, using sourcemaps.
17
+ if (vite) {
18
+ try {
19
+ vite.ssrFixStacktrace(safeError);
20
+ }
21
+ catch { }
22
+ }
23
+ if (filePath) {
24
+ const path = fileURLToPath(filePath);
25
+ const content = fs.readFileSync(path).toString();
26
+ const lns = content.split('\n');
27
+ // Vite has a fairly generic error message when it fails to load a module, let's try to enhance it a bit
28
+ // https://github.com/vitejs/vite/blob/ee7c28a46a6563d54b828af42570c55f16b15d2c/packages/vite/src/node/ssr/ssrModuleLoader.ts#L91
29
+ let importName;
30
+ if ((importName = /Failed to load url (.*?) \(resolved id:/.exec(safeError.message)?.[1])) {
31
+ safeError.title = FailedToLoadModuleSSR.title;
32
+ safeError.name = 'FailedToLoadModuleSSR';
33
+ safeError.message = FailedToLoadModuleSSR.message(importName);
34
+ safeError.hint = FailedToLoadModuleSSR.hint;
35
+ const line = lns.findIndex((ln) => ln.includes(importName));
36
+ console.log({ line });
37
+ if (line !== -1) {
38
+ const column = lns[line]?.indexOf(importName);
39
+ safeError.loc = {
40
+ file: path,
41
+ line: line + 1,
42
+ };
43
+ if (column)
44
+ safeError.loc.column = column;
45
+ }
46
+ }
47
+ // const fileId = safeError.id ?? safeError.loc?.file;
48
+ // // Vite throws a syntax error trying to parse MDX without a plugin.
49
+ // // Suggest installing the MDX integration if none is found.
50
+ // if (
51
+ // fileId &&
52
+ // !renderers?.find((r) => r.name === '@astrojs/mdx') &&
53
+ // safeError.message.includes('Syntax error') &&
54
+ // /.mdx$/.test(fileId)
55
+ // ) {
56
+ // safeError = new BetterError({
57
+ // ...MdxIntegrationMissingError,
58
+ // message: MdxIntegrationMissingError.message(JSON.stringify(fileId)),
59
+ // location: safeError.loc,
60
+ // stack: safeError.stack,
61
+ // }) as ErrorWithMetadata;
62
+ // }
63
+ // // Since Astro.glob is a wrapper around Vite's import.meta.glob, errors don't show accurate information, let's fix that
64
+ // if (safeError.message.includes('Invalid glob')) {
65
+ // const globPattern = /glob: "(.+)" \(/.exec(safeError.message)?.[1];
66
+ // if (globPattern) {
67
+ // safeError.message = InvalidGlob.message(globPattern);
68
+ // safeError.name = 'InvalidGlob';
69
+ // safeError.title = InvalidGlob.title;
70
+ // const line = lns.findIndex((ln) => ln.includes(globPattern));
71
+ // if (line !== -1) {
72
+ // const column = lns[line]?.indexOf(globPattern);
73
+ // safeError.loc = {
74
+ // file: path,
75
+ // line: line + 1,
76
+ // column,
77
+ // };
78
+ // }
79
+ // }
80
+ // }
81
+ }
82
+ return safeError;
83
+ }
84
+ // Shiki does not support `mjs` or `cjs` aliases by default.
85
+ // Map these to `.js` during error highlighting.
86
+ const ALTERNATIVE_JS_EXTS = ['cjs', 'mjs'];
87
+ const ALTERNATIVE_MD_EXTS = ['mdoc'];
88
+ // let _cssVariablesTheme: ReturnType<typeof createCssVariablesTheme>;
89
+ // const cssVariablesTheme = () =>
90
+ // _cssVariablesTheme ??
91
+ // (_cssVariablesTheme = createCssVariablesTheme({
92
+ // variablePrefix: '--astro-code-',
93
+ // }));
94
+ /**
95
+ * Generate a payload for Vite's error overlay
96
+ */
97
+ export async function getViteErrorPayload({ err, errorsData, docsBaseUrl, }) {
98
+ let plugin = err.plugin;
99
+ if (!plugin && err.hint) {
100
+ plugin = 'astro';
101
+ }
102
+ const message = renderErrorMarkdown(err.message.trim(), 'html');
103
+ const hint = err.hint
104
+ ? renderErrorMarkdown(err.hint.trim(), 'html')
105
+ : undefined;
106
+ const docslink = errorsData
107
+ ? getDocsForError(err, errorsData, docsBaseUrl)
108
+ : undefined;
109
+ // let highlighterLang = err.loc?.file?.split('.').pop();
110
+ // if (ALTERNATIVE_JS_EXTS.includes(highlighterLang ?? '')) {
111
+ // highlighterLang = 'js';
112
+ // } else if (ALTERNATIVE_MD_EXTS.includes(highlighterLang ?? '')) {
113
+ // highlighterLang = 'md';
114
+ // }
115
+ // const highlightedCode = err.fullCode
116
+ // ? await codeToHtml(err.fullCode, {
117
+ // lang: highlighterLang ?? 'text',
118
+ // theme: cssVariablesTheme(),
119
+ // transformers: [
120
+ // transformerCompactLineOptions(
121
+ // err.loc?.line
122
+ // ? [{ line: err.loc.line, classes: ['error-line'] }]
123
+ // : undefined,
124
+ // ),
125
+ // ],
126
+ // })
127
+ // : undefined;
128
+ const highlightedCode = err.fullCode
129
+ ? collectResultSync(render(html `
130
+ <div id="code-content">
131
+ <pre
132
+ class="shiki css-variables"
133
+ style="background-color:var(--betterr-code-background);color:var(--betterr-code-foreground)"
134
+ tabindex="0"
135
+ ><code>${err.fullCode
136
+ .split('\n')
137
+ .map((l, i) => html `<span
138
+ class="line ${err.loc?.line === i + 1
139
+ ? 'error-line'
140
+ : ''} "
141
+ >${l}</span
142
+ >${'\n'}`)}</code></pre>
143
+ </div>
144
+ `))
145
+ : undefined;
146
+ return {
147
+ type: 'error',
148
+ err: {
149
+ ...err,
150
+ name: err.name,
151
+ type: err.type,
152
+ message,
153
+ hint,
154
+ frame: err.frame,
155
+ highlightedCode,
156
+ docslink,
157
+ // @ts-expect-error Exact optional props
158
+ loc: {
159
+ file: err.loc?.file,
160
+ line: err.loc?.line,
161
+ column: err.loc?.column,
162
+ },
163
+ plugin,
164
+ stack: err.stack,
165
+ cause: err.cause,
166
+ },
167
+ };
168
+ }
169
+ // /**
170
+ // * Transformer for `shiki`'s legacy `lineOptions`, allows to add classes to specific lines
171
+ // * FROM: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/packages/transformers/src/transformers/compact-line-options.ts
172
+ // * LICENSE: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/LICENSE
173
+ // */
174
+ // function transformerCompactLineOptions(
175
+ // lineOptions: {
176
+ // /**
177
+ // * 1-based line number.
178
+ // */
179
+ // line: number;
180
+ // classes?: string[];
181
+ // }[] = [],
182
+ // ): ShikiTransformer {
183
+ // return {
184
+ // name: '@shikijs/transformers:compact-line-options',
185
+ // line(node, line) {
186
+ // const lineOption = lineOptions.find((o) => o.line === line);
187
+ // if (lineOption?.classes) this.addClassToHast(node, lineOption.classes);
188
+ // return node;
189
+ // },
190
+ // };
191
+ // }
@@ -0,0 +1,16 @@
1
+ export interface BetterErrorData {
2
+ name: string;
3
+ title: string;
4
+ message?: string | ((...params: any) => string) | undefined;
5
+ hint?: string | ((...params: any) => string) | undefined;
6
+ }
7
+ /**
8
+ *
9
+ */
10
+ export declare const FailedToLoadModuleSSR: {
11
+ name: string;
12
+ title: string;
13
+ message: (importName: string) => string;
14
+ hint: string;
15
+ };
16
+ //# sourceMappingURL=errors-data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors-data.d.ts","sourceRoot":"","sources":["../src/errors-data.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;IAC5D,IAAI,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,KAAK,MAAM,CAAC,GAAG,SAAS,CAAC;CACzD;AAED;;GAEG;AACH,eAAO,MAAM,qBAAqB;;;0BAGX,MAAM;;CAEF,CAAC"}
@@ -0,0 +1,10 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /**
3
+ *
4
+ */
5
+ export const FailedToLoadModuleSSR = {
6
+ name: 'FailedToLoadModuleSSR',
7
+ title: 'Could not import file.',
8
+ message: (importName) => `Could not import \`${importName}\`.`,
9
+ hint: 'This is often caused by a typo in the import path. Please make sure the file exists.',
10
+ };
@@ -0,0 +1,66 @@
1
+ import type { ErrorPayload } from 'vite';
2
+ export type BuiltinErrorTypes = 'BetterError' | 'InternalError' | 'AggregateError';
3
+ /**
4
+ * Generic object representing an error with all possible data
5
+ * Compatible with Gracile, Astro's and Vite's errors
6
+ */
7
+ export interface ErrorWithMetadata<T extends string = string> {
8
+ [name: string]: unknown;
9
+ name: string;
10
+ title?: string;
11
+ type?: BuiltinErrorTypes & T;
12
+ message: string;
13
+ stack: string;
14
+ hint?: string;
15
+ id?: string;
16
+ frame?: string;
17
+ plugin?: string;
18
+ pluginCode?: string;
19
+ fullCode?: string;
20
+ loc?: {
21
+ file?: string;
22
+ line?: number;
23
+ column?: number;
24
+ };
25
+ cause?: unknown;
26
+ }
27
+ interface ErrorProperties {
28
+ title?: string | undefined;
29
+ name: string;
30
+ message?: string | undefined;
31
+ location?: ErrorLocation | undefined;
32
+ hint?: string | undefined;
33
+ stack?: string | undefined;
34
+ frame?: string | undefined;
35
+ }
36
+ export interface ErrorLocation {
37
+ file?: string | undefined;
38
+ line?: number | undefined;
39
+ column?: number | undefined;
40
+ }
41
+ export declare class BetterError<ConsumerErrorTypes extends string = string> extends Error {
42
+ loc: ErrorLocation | undefined;
43
+ title: string | undefined;
44
+ hint: string | undefined;
45
+ frame: string | undefined;
46
+ type: BuiltinErrorTypes | ConsumerErrorTypes;
47
+ constructor(props: ErrorProperties, options?: ErrorOptions);
48
+ setLocation(location: ErrorLocation): void;
49
+ setName(name: string): void;
50
+ setMessage(message: string): void;
51
+ setHint(hint: string): void;
52
+ setFrame(source: string, location: ErrorLocation): void;
53
+ static is(err: unknown): err is BetterError;
54
+ }
55
+ export declare class AggregateError extends BetterError {
56
+ type: BuiltinErrorTypes;
57
+ errors: BetterError[];
58
+ constructor(props: ErrorProperties & {
59
+ errors: BetterError[];
60
+ }, options?: ErrorOptions);
61
+ static is(err: unknown): err is AggregateError;
62
+ }
63
+ export declare function isBetterError(e: unknown): e is BetterError;
64
+ export type SSRError = Error & ErrorPayload['err'];
65
+ export {};
66
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/errors.js ADDED
@@ -0,0 +1,51 @@
1
+ import { codeFrame } from './dev/printer.js';
2
+ export class BetterError extends Error {
3
+ constructor(props, options) {
4
+ const { name, title, message, stack, location, hint, frame } = props;
5
+ super(message, options);
6
+ this.type = 'BetterError';
7
+ this.title = title;
8
+ this.name = name;
9
+ if (message)
10
+ this.message = message;
11
+ // Only set this if we actually have a stack passed, otherwise uses Error's
12
+ if (stack)
13
+ this.stack = stack;
14
+ this.loc = location;
15
+ this.hint = hint;
16
+ this.frame = frame;
17
+ }
18
+ setLocation(location) {
19
+ this.loc = location;
20
+ }
21
+ setName(name) {
22
+ this.name = name;
23
+ }
24
+ setMessage(message) {
25
+ this.message = message;
26
+ }
27
+ setHint(hint) {
28
+ this.hint = hint;
29
+ }
30
+ setFrame(source, location) {
31
+ this.frame = codeFrame(source, location);
32
+ }
33
+ static is(err) {
34
+ return err.type === 'BetterError';
35
+ }
36
+ }
37
+ export class AggregateError extends BetterError {
38
+ // Despite being a collection of errors, AggregateError still needs to have a main error attached to it
39
+ // This is because Vite expects every thrown errors handled during HMR to be, well, Error and have a message
40
+ constructor(props, options) {
41
+ super(props, options);
42
+ this.type = 'AggregateError';
43
+ this.errors = props.errors;
44
+ }
45
+ static is(err) {
46
+ return err.type === 'AggregateError';
47
+ }
48
+ }
49
+ export function isBetterError(e) {
50
+ return e instanceof BetterError;
51
+ }
@@ -0,0 +1,5 @@
1
+ export declare function patchOverlay(code: string, importPath?: string): string;
2
+ export declare function betterErrors(options?: {
3
+ overlayImportPath: string;
4
+ }): any[];
5
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAeA,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,UA6B7D;AAED,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE;IACtC,iBAAiB,EAAE,MAAM,CAAC;CAK1B,GAAG,GAAG,EAAE,CAgER"}