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,
|
ComponentInstance,
|
||||||
EndpointHandler,
|
EndpointHandler,
|
||||||
RouteType,
|
RouteType,
|
||||||
|
SSRError,
|
||||||
SSRLoadedRenderer,
|
SSRLoadedRenderer,
|
||||||
} from '../../@types/astro';
|
} from '../../@types/astro';
|
||||||
import { getContentPaths } from '../../content/index.js';
|
import { getContentPaths } from '../../content/index.js';
|
||||||
|
@ -22,6 +23,7 @@ import {
|
||||||
import { runHookBuildGenerated } from '../../integrations/index.js';
|
import { runHookBuildGenerated } from '../../integrations/index.js';
|
||||||
import { BEFORE_HYDRATION_SCRIPT_ID, PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/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 { call as callEndpoint, throwIfRedirectNotAllowed } from '../endpoint/index.js';
|
||||||
|
import { AstroError } from '../errors/index.js';
|
||||||
import { debug, info } from '../logger/core.js';
|
import { debug, info } from '../logger/core.js';
|
||||||
import { createEnvironment, createRenderContext, renderPage } from '../render/index.js';
|
import { createEnvironment, createRenderContext, renderPage } from '../render/index.js';
|
||||||
import { callGetStaticPaths } from '../render/route-cache.js';
|
import { callGetStaticPaths } from '../render/route-cache.js';
|
||||||
|
@ -397,7 +399,15 @@ async function generatePath(
|
||||||
encoding = result.encoding;
|
encoding = result.encoding;
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
throwIfRedirectNotAllowed(response, opts.settings.config);
|
||||||
// If there's no body, do nothing
|
// If there's no body, do nothing
|
||||||
if (!response.body) return;
|
if (!response.body) return;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { DiagnosticCode } from '@astrojs/compiler/shared/diagnostics.js';
|
import { DiagnosticCode } from '@astrojs/compiler/shared/diagnostics.js';
|
||||||
|
import type { SSRError } from '../../@types/astro.js';
|
||||||
import { AstroErrorCodes, AstroErrorData } from './errors-data.js';
|
import { AstroErrorCodes, AstroErrorData } from './errors-data.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,9 +73,17 @@ function getLineOffsets(text: string) {
|
||||||
|
|
||||||
/** Coalesce any throw variable to an Error instance. */
|
/** Coalesce any throw variable to an Error instance. */
|
||||||
export function createSafeError(err: any): Error {
|
export function createSafeError(err: any): Error {
|
||||||
return err instanceof Error || (err && err.name && err.message)
|
if (err instanceof Error || (err && err.name && err.message)) {
|
||||||
? err
|
return err;
|
||||||
: new Error(JSON.stringify(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) {
|
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!")
|
||||||
|
---
|
6
pnpm-lock.yaml
generated
6
pnpm-lock.yaml
generated
|
@ -1781,6 +1781,12 @@ importers:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro: link:../../..
|
astro: link:../../..
|
||||||
|
|
||||||
|
packages/astro/test/fixtures/error-build-location:
|
||||||
|
specifiers:
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
astro: link:../../..
|
||||||
|
|
||||||
packages/astro/test/fixtures/error-non-error:
|
packages/astro/test/fixtures/error-non-error:
|
||||||
specifiers:
|
specifiers:
|
||||||
astro: workspace:*
|
astro: workspace:*
|
||||||
|
|
Loading…
Add table
Reference in a new issue