mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
fix: highlight line with error in the error overlay (#11574)
* fix: highlight line with error in the error overlay * chore: changeset * Update packages/astro/e2e/errors.test.js Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com> --------- Co-authored-by: Bjorn Lu <bjornlu.dev@gmail.com>
This commit is contained in:
parent
0dcef3ab17
commit
e3f29d416a
4 changed files with 50 additions and 6 deletions
5
.changeset/old-bats-travel.md
Normal file
5
.changeset/old-bats-travel.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes line with the error not being properly highlighted in the error overlay
|
|
@ -131,4 +131,12 @@ test.describe('Error display', () => {
|
||||||
const message = (await getErrorOverlayContent(page)).message;
|
const message = (await getErrorOverlayContent(page)).message;
|
||||||
expect(message).toMatch('The operation was aborted due to timeout');
|
expect(message).toMatch('The operation was aborted due to timeout');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('properly highlight the line with the error', async ({ page, astro }) => {
|
||||||
|
await page.goto(astro.resolveUrl('/import-not-found'), { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
const { codeFrame } = await getErrorOverlayContent(page);
|
||||||
|
const codeFrameContent = await codeFrame.innerHTML();
|
||||||
|
expect(codeFrameContent).toContain('error-line');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -51,8 +51,8 @@ export function testFactory(inlineConfig) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {string} page
|
* @param {import('@playwright/test').Page} page
|
||||||
* @returns {Promise<{message: string, hint: string, absoluteFileLocation: string, fileLocation: string}>}
|
* @returns {Promise<{message: string, hint: string, absoluteFileLocation: string, fileLocation: string, codeFrame: import('@playwright/test').ElementHandle}>}
|
||||||
*/
|
*/
|
||||||
export async function getErrorOverlayContent(page) {
|
export async function getErrorOverlayContent(page) {
|
||||||
const overlay = await page.waitForSelector('vite-error-overlay', {
|
const overlay = await page.waitForSelector('vite-error-overlay', {
|
||||||
|
@ -68,7 +68,10 @@ export async function getErrorOverlayContent(page) {
|
||||||
m[0].title,
|
m[0].title,
|
||||||
m[0].textContent,
|
m[0].textContent,
|
||||||
]);
|
]);
|
||||||
return { message, hint, absoluteFileLocation, fileLocation };
|
|
||||||
|
const codeFrame = await overlay.$('#code pre code');
|
||||||
|
|
||||||
|
return { message, hint, absoluteFileLocation, fileLocation, codeFrame };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { codeToHtml, createCssVariablesTheme } from 'shiki';
|
import { codeToHtml, createCssVariablesTheme } from 'shiki';
|
||||||
|
import type { ShikiTransformer } from 'shiki';
|
||||||
import type { ErrorPayload } from 'vite';
|
import type { ErrorPayload } from 'vite';
|
||||||
import type { ModuleLoader } from '../../module-loader/index.js';
|
import type { ModuleLoader } from '../../module-loader/index.js';
|
||||||
import { FailedToLoadModuleSSR, InvalidGlob, MdxIntegrationMissingError } from '../errors-data.js';
|
import { FailedToLoadModuleSSR, InvalidGlob, MdxIntegrationMissingError } from '../errors-data.js';
|
||||||
|
@ -151,10 +152,13 @@ export async function getViteErrorPayload(err: ErrorWithMetadata): Promise<Astro
|
||||||
}
|
}
|
||||||
const highlightedCode = err.fullCode
|
const highlightedCode = err.fullCode
|
||||||
? await codeToHtml(err.fullCode, {
|
? await codeToHtml(err.fullCode, {
|
||||||
// @ts-expect-error always assume that shiki can accept the lang string
|
lang: highlighterLang ?? 'text',
|
||||||
lang: highlighterLang,
|
|
||||||
theme: cssVariablesTheme(),
|
theme: cssVariablesTheme(),
|
||||||
lineOptions: err.loc?.line ? [{ line: err.loc.line, classes: ['error-line'] }] : undefined,
|
transformers: [
|
||||||
|
transformerCompactLineOptions(
|
||||||
|
err.loc?.line ? [{ line: err.loc.line, classes: ['error-line'] }] : undefined
|
||||||
|
),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
@ -180,3 +184,27 @@ export async function getViteErrorPayload(err: ErrorWithMetadata): Promise<Astro
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformer for `shiki`'s legacy `lineOptions`, allows to add classes to specific lines
|
||||||
|
* FROM: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/packages/transformers/src/transformers/compact-line-options.ts
|
||||||
|
* LICENSE: https://github.com/shikijs/shiki/blob/4a58472070a9a359a4deafec23bb576a73e24c6a/LICENSE
|
||||||
|
*/
|
||||||
|
export function transformerCompactLineOptions(
|
||||||
|
lineOptions: {
|
||||||
|
/**
|
||||||
|
* 1-based line number.
|
||||||
|
*/
|
||||||
|
line: number;
|
||||||
|
classes?: string[];
|
||||||
|
}[] = []
|
||||||
|
): ShikiTransformer {
|
||||||
|
return {
|
||||||
|
name: '@shikijs/transformers:compact-line-options',
|
||||||
|
line(node, line) {
|
||||||
|
const lineOption = lineOptions.find((o) => o.line === line);
|
||||||
|
if (lineOption?.classes) this.addClassToHast(node, lineOption.classes);
|
||||||
|
return node;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue