mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
Support 500 pages in the dev server (#131)
* Support 500 pages * Document custom 400/500 pages * Remove search from any pages not the 500 page * fix(kitchen-sink): add snowpack.config.js * fix(examples): add snowpack.config.js * style: redesign built-in 500 page Co-authored-by: Nate Moore <nate@skypack.dev>
This commit is contained in:
parent
87af0aead8
commit
0ea4a986e2
12 changed files with 233 additions and 15 deletions
10
README.md
10
README.md
|
@ -210,9 +210,18 @@ Now upload the contents of `/_site_` to your favorite static site host.
|
||||||
|
|
||||||
👉 [**Full API Reference**][docs-api]
|
👉 [**Full API Reference**][docs-api]
|
||||||
|
|
||||||
|
## 👩🏽💻 CLI
|
||||||
|
|
||||||
|
👉 [**Command Line Docs**][docs-cli]
|
||||||
|
|
||||||
|
## 🏗 Development Server
|
||||||
|
|
||||||
|
👉 [**Dev Server Docs**][docs-dev]
|
||||||
|
|
||||||
[config]: #%EF%B8%8F-configuration
|
[config]: #%EF%B8%8F-configuration
|
||||||
[docs-api]: ./docs/api.md
|
[docs-api]: ./docs/api.md
|
||||||
[docs-collections]: ./docs/collections.md
|
[docs-collections]: ./docs/collections.md
|
||||||
|
[docs-dev]: ./docs/dev.md
|
||||||
[docs-styling]: ./docs/styling.md
|
[docs-styling]: ./docs/styling.md
|
||||||
[example-blog]: ./examples/blog
|
[example-blog]: ./examples/blog
|
||||||
[fetch-content]: ./docs/api.md#fetchcontent
|
[fetch-content]: ./docs/api.md#fetchcontent
|
||||||
|
@ -220,3 +229,4 @@ Now upload the contents of `/_site_` to your favorite static site host.
|
||||||
[mdn-io]: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
|
[mdn-io]: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
|
||||||
[mdn-ric]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
[mdn-ric]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback
|
||||||
[routing]: #-routing
|
[routing]: #-routing
|
||||||
|
[docs-cli]: ./docs/cli.md
|
||||||
|
|
|
@ -34,6 +34,8 @@ Print the help message and exit.
|
||||||
|
|
||||||
Runs the Astro development server. This starts an HTTP server that responds to requests for pages stored in `astro/pages` (or which folder is specified in your [configuration](../README.md##%EF%B8%8F-configuration)).
|
Runs the Astro development server. This starts an HTTP server that responds to requests for pages stored in `astro/pages` (or which folder is specified in your [configuration](../README.md##%EF%B8%8F-configuration)).
|
||||||
|
|
||||||
|
See the [dev server](./dev.md) docs for more information on how the dev server works.
|
||||||
|
|
||||||
__Flags__
|
__Flags__
|
||||||
|
|
||||||
##### `--port`
|
##### `--port`
|
||||||
|
|
51
docs/dev.md
Normal file
51
docs/dev.md
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
# Development Server
|
||||||
|
|
||||||
|
The development server comes as part of the Astro CLI. Start the server with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
astro dev
|
||||||
|
```
|
||||||
|
|
||||||
|
In your project root. You can specify an alternative
|
||||||
|
|
||||||
|
## Special routes
|
||||||
|
|
||||||
|
The dev server will serve the following special routes:
|
||||||
|
|
||||||
|
### /400
|
||||||
|
|
||||||
|
This is a custom __400__ status code page. You can add this route by adding a page component to your `astro/pages` folder:
|
||||||
|
|
||||||
|
```
|
||||||
|
├── astro/
|
||||||
|
│ ├── components/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── 400.astro
|
||||||
|
```
|
||||||
|
|
||||||
|
For any URL you visit that doesn't have a corresponding page, the `400.astro` file will be used.
|
||||||
|
|
||||||
|
### /500
|
||||||
|
|
||||||
|
This is a custom __500__ status code page. You can add this route by adding a page component to your `astro/pages` folder:
|
||||||
|
|
||||||
|
```astro
|
||||||
|
├── astro/
|
||||||
|
│ ├── components/
|
||||||
|
│ └── pages/
|
||||||
|
│ └── 500.astro
|
||||||
|
```
|
||||||
|
|
||||||
|
This page is used any time an error occurs in the dev server.
|
||||||
|
|
||||||
|
The 500 page will receive an `error` query parameter which you can access with:
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
const error = import.meta.request.url.searchParams.get('error');
|
||||||
|
---
|
||||||
|
|
||||||
|
<strong>{error}</strong>
|
||||||
|
```
|
||||||
|
|
||||||
|
A default error page is included with Astro so you will get pretty error messages even without adding a custom 500 page.
|
3
examples/blog/snowpack.config.js
Normal file
3
examples/blog/snowpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
workspaceRoot: '../../'
|
||||||
|
};
|
3
examples/kitchen-sink/snowpack.config.js
Normal file
3
examples/kitchen-sink/snowpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
workspaceRoot: '../../'
|
||||||
|
};
|
1
examples/snowpack/package-lock.json
generated
1
examples/snowpack/package-lock.json
generated
|
@ -975,6 +975,7 @@
|
||||||
"astro": {
|
"astro": {
|
||||||
"version": "file:../..",
|
"version": "file:../..",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"@babel/code-frame": "^7.12.13",
|
||||||
"@babel/generator": "^7.13.9",
|
"@babel/generator": "^7.13.9",
|
||||||
"@babel/parser": "^7.13.15",
|
"@babel/parser": "^7.13.15",
|
||||||
"@babel/traverse": "^7.13.15",
|
"@babel/traverse": "^7.13.15",
|
||||||
|
|
3
examples/tailwindcss/snowpack.config.js
Normal file
3
examples/tailwindcss/snowpack.config.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
workspaceRoot: '../../'
|
||||||
|
};
|
|
@ -22,7 +22,7 @@
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build:core && npm run build:parser",
|
"build": "npm run build:core && npm run build:parser",
|
||||||
"build:core": "tsc -p tsconfig.json",
|
"build:core": "tsc -p tsconfig.json && cp src/frontend/500.astro lib/frontend/500.astro",
|
||||||
"build:parser": "tsc -p tsconfig.parser.json",
|
"build:parser": "tsc -p tsconfig.parser.json",
|
||||||
"postbuild:parser": "echo '{ \"type\": \"commonjs\" }' > parser/package.json",
|
"postbuild:parser": "echo '{ \"type\": \"commonjs\" }' > parser/package.json",
|
||||||
"dev": "tsc --watch",
|
"dev": "tsc --watch",
|
||||||
|
|
18
src/dev.ts
18
src/dev.ts
|
@ -72,7 +72,17 @@ export default async function dev(astroConfig: AstroConfig) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.statusCode = 500;
|
res.statusCode = 500;
|
||||||
res.end(formatErrorForBrowser(result.error));
|
|
||||||
|
let errorResult = await runtime.load(`/500?error=${encodeURIComponent(result.error.stack || result.error.toString())}`);
|
||||||
|
if(errorResult.statusCode === 200) {
|
||||||
|
if (errorResult.contentType) {
|
||||||
|
res.setHeader('Content-Type', errorResult.contentType);
|
||||||
|
}
|
||||||
|
res.write(errorResult.contents);
|
||||||
|
} else {
|
||||||
|
res.write(result.error.toString());
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,9 +95,3 @@ export default async function dev(astroConfig: AstroConfig) {
|
||||||
info(logging, 'dev server', `${green('Local:')} http://${hostname}:${port}/`);
|
info(logging, 'dev server', `${green('Local:')} http://${hostname}:${port}/`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Format error message */
|
|
||||||
function formatErrorForBrowser(err: Error) {
|
|
||||||
// TODO make this pretty.
|
|
||||||
return err.toString();
|
|
||||||
}
|
|
||||||
|
|
128
src/frontend/500.astro
Normal file
128
src/frontend/500.astro
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
---
|
||||||
|
import Prism from 'astro/components/Prism.astro';
|
||||||
|
let title = 'Uh oh...';
|
||||||
|
|
||||||
|
const error = import.meta.request.url.searchParams.get('error');
|
||||||
|
---
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Error 500</title>
|
||||||
|
<link rel="preconnect"href="https://fonts.gstatic.com">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=IBM+Plex+Sans&display=swap">
|
||||||
|
<link rel="stylesheet" href="http://cdn.skypack.dev/prism-themes/themes/prism-material-dark.css">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(:root) {
|
||||||
|
--font-sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
|
--font-mono: "IBM Plex Mono", Consolas, "Andale Mono WT", "Andale Mono",
|
||||||
|
"Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono",
|
||||||
|
"Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco,
|
||||||
|
"Courier New", Courier, monospace;
|
||||||
|
--color-gray-800: #1F2937;
|
||||||
|
--color-gray-500: #6B7280;
|
||||||
|
--color-gray-400: #9CA3AF;
|
||||||
|
--color-gray-100: #F3F4F6;
|
||||||
|
--color-red: #FF1639;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100vh;
|
||||||
|
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-weight: 400;
|
||||||
|
background: var(--color-gray-100);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: grid;
|
||||||
|
place-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
font-family: var(--font-sans);
|
||||||
|
font-size: 2.5rem;
|
||||||
|
font-size: clamp(24px, calc(2vw + 1rem), 2.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0.25em;
|
||||||
|
margin-right: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 .title {
|
||||||
|
color: var(--color-gray-400);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
header svg {
|
||||||
|
margin-bottom: -0.125em;
|
||||||
|
color: var(--color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
font-size: clamp(14px, calc(2vw + 0.5rem), 1.75rem);
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
display: grid;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message :global(code[class*="language-"]) {
|
||||||
|
background: var(--color-gray-800);
|
||||||
|
}
|
||||||
|
.error-message :global(pre) {
|
||||||
|
margin: 0;
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
background: var(--color-gray-800);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message :global(.token.punctuation) {
|
||||||
|
color: var(--color-gray-400);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message :global(.token.operator) {
|
||||||
|
color: var(--color-gray-400);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<header>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" width="1.75em" height="1.75em">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||||
|
</svg>
|
||||||
|
<h1><span class="error">500 Error </span><span class="title">{title}</span></h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<article>
|
||||||
|
<p>Astro had some trouble loading this page.</p>
|
||||||
|
|
||||||
|
<div class="error-message">
|
||||||
|
<Prism lang="shell" code={error} />
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -195,11 +195,20 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro
|
||||||
collection.data = data;
|
collection.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestURL = new URL(fullurl.toString());
|
||||||
|
|
||||||
|
// For first release query params are not passed to components.
|
||||||
|
// An exception is made for dev server specific routes.
|
||||||
|
if(reqPath !== '/500') {
|
||||||
|
requestURL.search = '';
|
||||||
|
}
|
||||||
|
|
||||||
let html = (await mod.exports.__renderPage({
|
let html = (await mod.exports.__renderPage({
|
||||||
request: {
|
request: {
|
||||||
host: fullurl.hostname,
|
host: fullurl.hostname,
|
||||||
path: fullurl.pathname,
|
path: fullurl.pathname,
|
||||||
href: fullurl.toString(),
|
href: fullurl.toString(),
|
||||||
|
url: requestURL
|
||||||
},
|
},
|
||||||
children: [],
|
children: [],
|
||||||
props: { collection },
|
props: { collection },
|
||||||
|
|
|
@ -91,6 +91,17 @@ export function searchForPage(url: URL, astroRoot: URL): SearchResult {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(reqPath === '/500') {
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
location: {
|
||||||
|
fileURL: new URL('./frontend/500.astro', import.meta.url),
|
||||||
|
snowpackURL: `/_astro_internal/500.astro.js`
|
||||||
|
},
|
||||||
|
pathname: reqPath
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
};
|
};
|
||||||
|
@ -128,10 +139,3 @@ function loadCollection(url: string, astroRoot: URL): { currentPage?: number; lo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** convert a value to a number, if possible */
|
|
||||||
function maybeNum(val: string): string | number {
|
|
||||||
const num = parseFloat(val);
|
|
||||||
if (num.toString() === val) return num;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue