404 page (#1811)
* Redesigned 404 page * Update 5xx template Co-authored-by: Isaac McFadyen <6243993+mcfadyeni@users.noreply.github.com>
This commit is contained in:
parent
48ebbb80d4
commit
2b031acbd7
5 changed files with 176 additions and 70 deletions
|
@ -19,7 +19,8 @@ import { collectResources } from '../ssr/html.js';
|
||||||
import { createRouteManifest, matchRoute } from '../ssr/routing.js';
|
import { createRouteManifest, matchRoute } from '../ssr/routing.js';
|
||||||
import { createVite } from '../create-vite.js';
|
import { createVite } from '../create-vite.js';
|
||||||
import * as msg from './messages.js';
|
import * as msg from './messages.js';
|
||||||
import { errorTemplate } from './template/error.js';
|
import notFoundTemplate from './template/4xx.js';
|
||||||
|
import serverErrorTemplate from './template/5xx.js';
|
||||||
|
|
||||||
export interface DevOptions {
|
export interface DevOptions {
|
||||||
logging: LogOptions;
|
logging: LogOptions;
|
||||||
|
@ -303,7 +304,7 @@ export class AstroDevServer {
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
this.viteServer.ws.send({ type: 'error', err });
|
this.viteServer.ws.send({ type: 'error', err });
|
||||||
const statusCode = 500;
|
const statusCode = 500;
|
||||||
const html = errorTemplate({
|
const html = serverErrorTemplate({
|
||||||
statusCode,
|
statusCode,
|
||||||
title: 'Internal Error',
|
title: 'Internal Error',
|
||||||
tabTitle: '500: Error',
|
tabTitle: '500: Error',
|
||||||
|
@ -347,7 +348,7 @@ export class AstroDevServer {
|
||||||
}
|
}
|
||||||
// if not found, fall back to default template
|
// if not found, fall back to default template
|
||||||
else {
|
else {
|
||||||
html = errorTemplate({ statusCode, title: 'Not found', tabTitle: '404: Not Found', message: pathname });
|
html = notFoundTemplate({ statusCode, title: 'Not found', tabTitle: '404: Not Found', pathname });
|
||||||
}
|
}
|
||||||
info(this.logging, 'astro', msg.req({ url: pathname, statusCode, reqTime: performance.now() - reqStart }));
|
info(this.logging, 'astro', msg.req({ url: pathname, statusCode, reqTime: performance.now() - reqStart }));
|
||||||
res.writeHead(statusCode, {
|
res.writeHead(statusCode, {
|
||||||
|
|
51
packages/astro/src/core/dev/template/4xx.ts
Normal file
51
packages/astro/src/core/dev/template/4xx.ts
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import { encode } from 'html-entities';
|
||||||
|
import { baseCSS } from './css.js';
|
||||||
|
interface ErrorTemplateOptions {
|
||||||
|
/** a short description of the error */
|
||||||
|
pathname: string;
|
||||||
|
/** HTTP error code */
|
||||||
|
statusCode?: number;
|
||||||
|
/** HTML <title> */
|
||||||
|
tabTitle: string;
|
||||||
|
/** page title */
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Display all errors */
|
||||||
|
export default function template({ title, pathname, statusCode = 404, tabTitle }: ErrorTemplateOptions): string {
|
||||||
|
return `<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>${tabTitle}</title>
|
||||||
|
<style>
|
||||||
|
${baseCSS}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusCode {
|
||||||
|
color: var(--orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
.astro {
|
||||||
|
height: 120px;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="center">
|
||||||
|
<svg class="astro" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" fill="#ff5d01"></path></svg>
|
||||||
|
<h1>${statusCode ? `<span class="statusCode">${statusCode}: </span> ` : ''}<span class="statusMessage">${title}</span></h1>
|
||||||
|
<pre>Path: ${encode(pathname)}</pre>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
71
packages/astro/src/core/dev/template/5xx.ts
Normal file
71
packages/astro/src/core/dev/template/5xx.ts
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
import { encode } from 'html-entities';
|
||||||
|
import { baseCSS } from './css.js';
|
||||||
|
|
||||||
|
interface ErrorTemplateOptions {
|
||||||
|
/** a short description of the error */
|
||||||
|
message: string;
|
||||||
|
/** information about where the error occurred */
|
||||||
|
stack?: string;
|
||||||
|
/** HTTP error code */
|
||||||
|
statusCode?: number;
|
||||||
|
/** HTML <title> */
|
||||||
|
tabTitle: string;
|
||||||
|
/** page title */
|
||||||
|
title: string;
|
||||||
|
/** show user a URL for more info or action to take */
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Display all errors */
|
||||||
|
export default function template({ title, url, message, stack, statusCode, tabTitle }: ErrorTemplateOptions): string {
|
||||||
|
let error = url ? message.replace(url, '') : message;
|
||||||
|
return `<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>${tabTitle}</title>
|
||||||
|
<style>
|
||||||
|
${baseCSS}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
max-width: 80rem;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
padding-right: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statusCode {
|
||||||
|
color: var(--orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
margin-top: 4rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.astro {
|
||||||
|
height: 4rem;
|
||||||
|
width: 4rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="wrapper">
|
||||||
|
<header>
|
||||||
|
<svg class="astro" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M163.008 18.929c1.944 2.413 2.935 5.67 4.917 12.181l43.309 142.27a180.277 180.277 0 00-51.778-17.53l-28.198-95.29a3.67 3.67 0 00-7.042.01l-27.857 95.232a180.225 180.225 0 00-52.01 17.557l43.52-142.281c1.99-6.502 2.983-9.752 4.927-12.16a15.999 15.999 0 016.484-4.798c2.872-1.154 6.271-1.154 13.07-1.154h31.085c6.807 0 10.211 0 13.086 1.157a16.004 16.004 0 016.487 4.806z" fill="white"></path><path fill-rule="evenodd" clip-rule="evenodd" d="M168.19 180.151c-7.139 6.105-21.39 10.268-37.804 10.268-20.147 0-37.033-6.272-41.513-14.707-1.602 4.835-1.961 10.367-1.961 13.902 0 0-1.056 17.355 11.015 29.426 0-6.268 5.081-11.349 11.349-11.349 10.743 0 10.731 9.373 10.721 16.977v.679c0 11.542 7.054 21.436 17.086 25.606a23.27 23.27 0 01-2.339-10.2c0-11.008 6.463-15.107 13.974-19.87 5.976-3.79 12.616-8.001 17.192-16.449a31.024 31.024 0 003.743-14.82c0-3.299-.513-6.479-1.463-9.463z" fill="#ff5d01"></path></svg>
|
||||||
|
<h1>${statusCode ? `<span class="statusCode">${statusCode}: </span> ` : ''}<span class="statusMessage">${title}</span></h1>
|
||||||
|
</header>
|
||||||
|
<pre>${encode(error)}</pre>
|
||||||
|
${url ? `<a target="_blank" href="${url}">${url}</a>` : ''}
|
||||||
|
<pre>${encode(stack)}</pre>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
50
packages/astro/src/core/dev/template/css.ts
Normal file
50
packages/astro/src/core/dev/template/css.ts
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* CSS is exported as a string so the error pages:
|
||||||
|
* 1. don’t need to resolve a deep internal CSS import
|
||||||
|
* 2. don’t need external dependencies to render (they may be shown because of a dep!)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Base CSS: shared CSS among pages
|
||||||
|
export const baseCSS = `
|
||||||
|
:root {
|
||||||
|
--gray-10: hsl(258, 7%, 10%);
|
||||||
|
--gray-20: hsl(258, 7%, 20%);
|
||||||
|
--gray-30: hsl(258, 7%, 30%);
|
||||||
|
--gray-40: hsl(258, 7%, 40%);
|
||||||
|
--gray-50: hsl(258, 7%, 50%);
|
||||||
|
--gray-60: hsl(258, 7%, 60%);
|
||||||
|
--gray-70: hsl(258, 7%, 70%);
|
||||||
|
--gray-80: hsl(258, 7%, 80%);
|
||||||
|
--gray-90: hsl(258, 7%, 90%);
|
||||||
|
--orange: #ff5d01;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: var(--gray-10);
|
||||||
|
color: var(--gray-80);
|
||||||
|
font-family: monospace;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--orange);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-weight: 800;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
color:;
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin-top: 0;
|
||||||
|
max-width: 60em;
|
||||||
|
}
|
||||||
|
`;
|
|
@ -1,67 +0,0 @@
|
||||||
import { encode } from 'html-entities';
|
|
||||||
|
|
||||||
interface ErrorTemplateOptions {
|
|
||||||
/** a short description of the error */
|
|
||||||
message: string;
|
|
||||||
/** information about where the error occurred */
|
|
||||||
stack?: string;
|
|
||||||
/** HTTP error code */
|
|
||||||
statusCode?: number;
|
|
||||||
/** HTML <title> */
|
|
||||||
tabTitle: string;
|
|
||||||
/** page title */
|
|
||||||
title: string;
|
|
||||||
/** show user a URL for more info or action to take */
|
|
||||||
url?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Display internal 404 page (if user didn’t provide one) */
|
|
||||||
export function errorTemplate({ title, url, message, stack, statusCode, tabTitle }: ErrorTemplateOptions): string {
|
|
||||||
let error = url ? message.replace(url, '') : message;
|
|
||||||
return `<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>${tabTitle}</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #101010;
|
|
||||||
color: #d0d0d0;
|
|
||||||
font-family: monospace;
|
|
||||||
line-height: 1.5;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.wrapper {
|
|
||||||
padding-left: 2rem;
|
|
||||||
padding-right: 2rem;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #ff5d01;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
font-weight: 800;
|
|
||||||
margin-top: 1rem;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
pre {
|
|
||||||
color: #999;
|
|
||||||
font-size: 1.2em;
|
|
||||||
margin-top: 0;
|
|
||||||
max-width: 60em;
|
|
||||||
}
|
|
||||||
.status {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<main class="wrapper">
|
|
||||||
<h1>${statusCode ? `<span class="statusCode">${statusCode}</span> ` : ''}${title}</h1>
|
|
||||||
<pre><code>${encode(error)}</code></pre>
|
|
||||||
${url ? `<a target="_blank" href="${url}">${url}</a>` : ''}
|
|
||||||
<pre><code>${encode(stack)}</code></pre>
|
|
||||||
</main>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
`;
|
|
||||||
}
|
|
Loading…
Reference in a new issue