diff options
author | 2021-04-26 16:42:11 -0400 | |
---|---|---|
committer | 2021-04-26 16:42:11 -0400 | |
commit | 0ea4a986e207238bf0ac1db841b2a5d5b567d84d (patch) | |
tree | 17246b70fd1ab907887bea778790c2f150485c2a | |
parent | 87af0aead8db809ffea402cfc3619de8190c6c16 (diff) | |
download | astro-0ea4a986e207238bf0ac1db841b2a5d5b567d84d.tar.gz astro-0ea4a986e207238bf0ac1db841b2a5d5b567d84d.tar.zst astro-0ea4a986e207238bf0ac1db841b2a5d5b567d84d.zip |
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>
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | docs/cli.md | 2 | ||||
-rw-r--r-- | docs/dev.md | 51 | ||||
-rw-r--r-- | examples/blog/snowpack.config.js | 3 | ||||
-rw-r--r-- | examples/kitchen-sink/snowpack.config.js | 3 | ||||
-rw-r--r-- | examples/snowpack/package-lock.json | 1 | ||||
-rw-r--r-- | examples/tailwindcss/snowpack.config.js | 3 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/dev.ts | 18 | ||||
-rw-r--r-- | src/frontend/500.astro | 128 | ||||
-rw-r--r-- | src/runtime.ts | 9 | ||||
-rw-r--r-- | src/search.ts | 18 |
12 files changed, 233 insertions, 15 deletions
@@ -210,9 +210,18 @@ Now upload the contents of `/_site_` to your favorite static site host. 👉 [**Full API Reference**][docs-api] +## 👩🏽💻 CLI + +👉 [**Command Line Docs**][docs-cli] + +## 🏗 Development Server + +👉 [**Dev Server Docs**][docs-dev] + [config]: #%EF%B8%8F-configuration [docs-api]: ./docs/api.md [docs-collections]: ./docs/collections.md +[docs-dev]: ./docs/dev.md [docs-styling]: ./docs/styling.md [example-blog]: ./examples/blog [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-ric]: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback [routing]: #-routing +[docs-cli]: ./docs/cli.md diff --git a/docs/cli.md b/docs/cli.md index c413323d5..67f0ae631 100644 --- a/docs/cli.md +++ b/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)). +See the [dev server](./dev.md) docs for more information on how the dev server works. + __Flags__ ##### `--port` diff --git a/docs/dev.md b/docs/dev.md new file mode 100644 index 000000000..7b811d7d3 --- /dev/null +++ b/docs/dev.md @@ -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.
\ No newline at end of file diff --git a/examples/blog/snowpack.config.js b/examples/blog/snowpack.config.js new file mode 100644 index 000000000..5a800b10f --- /dev/null +++ b/examples/blog/snowpack.config.js @@ -0,0 +1,3 @@ +module.exports = { + workspaceRoot: '../../' +}; diff --git a/examples/kitchen-sink/snowpack.config.js b/examples/kitchen-sink/snowpack.config.js new file mode 100644 index 000000000..5a800b10f --- /dev/null +++ b/examples/kitchen-sink/snowpack.config.js @@ -0,0 +1,3 @@ +module.exports = { + workspaceRoot: '../../' +}; diff --git a/examples/snowpack/package-lock.json b/examples/snowpack/package-lock.json index b802bc687..22de83842 100644 --- a/examples/snowpack/package-lock.json +++ b/examples/snowpack/package-lock.json @@ -975,6 +975,7 @@ "astro": { "version": "file:../..", "requires": { + "@babel/code-frame": "^7.12.13", "@babel/generator": "^7.13.9", "@babel/parser": "^7.13.15", "@babel/traverse": "^7.13.15", diff --git a/examples/tailwindcss/snowpack.config.js b/examples/tailwindcss/snowpack.config.js new file mode 100644 index 000000000..5a800b10f --- /dev/null +++ b/examples/tailwindcss/snowpack.config.js @@ -0,0 +1,3 @@ +module.exports = { + workspaceRoot: '../../' +}; diff --git a/package.json b/package.json index dbb929b3e..f8a489571 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ ], "scripts": { "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", "postbuild:parser": "echo '{ \"type\": \"commonjs\" }' > parser/package.json", "dev": "tsc --watch", diff --git a/src/dev.ts b/src/dev.ts index 505a99425..4ca8e28e9 100644 --- a/src/dev.ts +++ b/src/dev.ts @@ -72,7 +72,17 @@ export default async function dev(astroConfig: AstroConfig) { } } 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; } } @@ -85,9 +95,3 @@ export default async function dev(astroConfig: AstroConfig) { info(logging, 'dev server', `${green('Local:')} http://${hostname}:${port}/`); }); } - -/** Format error message */ -function formatErrorForBrowser(err: Error) { - // TODO make this pretty. - return err.toString(); -} diff --git a/src/frontend/500.astro b/src/frontend/500.astro new file mode 100644 index 000000000..af7b901e9 --- /dev/null +++ b/src/frontend/500.astro @@ -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> diff --git a/src/runtime.ts b/src/runtime.ts index 330ca013b..a394d93c4 100644 --- a/src/runtime.ts +++ b/src/runtime.ts @@ -195,11 +195,20 @@ async function load(config: RuntimeConfig, rawPathname: string | undefined): Pro 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({ request: { host: fullurl.hostname, path: fullurl.pathname, href: fullurl.toString(), + url: requestURL }, children: [], props: { collection }, diff --git a/src/search.ts b/src/search.ts index d4ed73f96..c141e4a77 100644 --- a/src/search.ts +++ b/src/search.ts @@ -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 { 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; -} |