Add file location to errors in build (#5743)
* feat(errors): Add file location where error happened during build for user-generated errors * chore: changeset * fix(errors): Only add information if the error is in a compatible shape * feat(errors): Add hint to throw Error objects instead of other types for better information * test(errors): Add test to make sure errors in build have the error location * chore(lockfile): Update lockfile * chore: misc text fixes
This commit is contained in:
parent
000d3e6940
commit
2a57864195
9 changed files with 78 additions and 4 deletions
5
.changeset/shaggy-melons-tap.md
Normal file
5
.changeset/shaggy-melons-tap.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Add error location during build for user-generated errors
|
|
@ -10,6 +10,7 @@ import type {
|
|||
ComponentInstance,
|
||||
EndpointHandler,
|
||||
RouteType,
|
||||
SSRError,
|
||||
SSRLoadedRenderer,
|
||||
} from '../../@types/astro';
|
||||
import { getContentPaths } from '../../content/index.js';
|
||||
|
@ -22,6 +23,7 @@ import {
|
|||
import { runHookBuildGenerated } from '../../integrations/index.js';
|
||||
import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
|
||||
import { call as callEndpoint, throwIfRedirectNotAllowed } from '../endpoint/index.js';
|
||||
import { AstroError } from '../errors/index.js';
|
||||
import { debug, info } from '../logger/core.js';
|
||||
import { createEnvironment, createRenderContext, renderPage } from '../render/index.js';
|
||||
import { callGetStaticPaths } from '../render/route-cache.js';
|
||||
|
@ -397,7 +399,15 @@ async function generatePath(
|
|||
encoding = result.encoding;
|
||||
}
|
||||
} else {
|
||||
const response = await renderPage(mod, ctx, env);
|
||||
let response: Response;
|
||||
try {
|
||||
response = await renderPage(mod, ctx, env);
|
||||
} catch (err) {
|
||||
if (!AstroError.is(err) && !(err as SSRError).id && typeof err === 'object') {
|
||||
(err as SSRError).id = pageData.component;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
throwIfRedirectNotAllowed(response, opts.settings.config);
|
||||
// If there's no body, do nothing
|
||||
if (!response.body) return;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { DiagnosticCode } from '@astrojs/compiler/shared/diagnostics.js';
|
||||
import type { SSRError } from '../../@types/astro.js';
|
||||
import { AstroErrorCodes, AstroErrorData } from './errors-data.js';
|
||||
|
||||
/**
|
||||
|
@ -72,9 +73,17 @@ function getLineOffsets(text: string) {
|
|||
|
||||
/** Coalesce any throw variable to an Error instance. */
|
||||
export function createSafeError(err: any): Error {
|
||||
return err instanceof Error || (err && err.name && err.message)
|
||||
? err
|
||||
: new Error(JSON.stringify(err));
|
||||
if (err instanceof Error || (err && err.name && err.message)) {
|
||||
return err;
|
||||
} else {
|
||||
const error = new Error(JSON.stringify(err));
|
||||
|
||||
(
|
||||
error as SSRError
|
||||
).hint = `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.`;
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeLF(code: string) {
|
||||
|
|
22
packages/astro/test/error-build-location.test.js
Normal file
22
packages/astro/test/error-build-location.test.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Errors information in build', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
it('includes the file where the error happened', async () => {
|
||||
fixture = await loadFixture({
|
||||
root: './fixtures/error-build-location',
|
||||
});
|
||||
|
||||
let errorContent;
|
||||
try {
|
||||
await fixture.build();
|
||||
} catch (e) {
|
||||
errorContent = e;
|
||||
}
|
||||
|
||||
expect(errorContent.id).to.equal('src/pages/index.astro');
|
||||
});
|
||||
});
|
3
packages/astro/test/fixtures/error-build-location/astro.config.mjs
vendored
Normal file
3
packages/astro/test/fixtures/error-build-location/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({});
|
15
packages/astro/test/fixtures/error-build-location/package.json
vendored
Normal file
15
packages/astro/test/fixtures/error-build-location/package.json
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "@test/error-non-error",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro": "workspace:*"
|
||||
}
|
||||
}
|
1
packages/astro/test/fixtures/error-build-location/src/env.d.ts
vendored
Normal file
1
packages/astro/test/fixtures/error-build-location/src/env.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/// <reference types="astro/client" />
|
3
packages/astro/test/fixtures/error-build-location/src/pages/index.astro
vendored
Normal file
3
packages/astro/test/fixtures/error-build-location/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
---
|
||||
throw new Error("I'm happening in build!")
|
||||
---
|
|
@ -1781,6 +1781,12 @@ importers:
|
|||
dependencies:
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/error-build-location:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
dependencies:
|
||||
astro: link:../../..
|
||||
|
||||
packages/astro/test/fixtures/error-non-error:
|
||||
specifiers:
|
||||
astro: workspace:*
|
||||
|
|
Loading…
Reference in a new issue