summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@matthewphillips.info> 2021-04-26 16:42:11 -0400
committerGravatar GitHub <noreply@github.com> 2021-04-26 16:42:11 -0400
commit0ea4a986e207238bf0ac1db841b2a5d5b567d84d (patch)
tree17246b70fd1ab907887bea778790c2f150485c2a
parent87af0aead8db809ffea402cfc3619de8190c6c16 (diff)
downloadastro-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.md10
-rw-r--r--docs/cli.md2
-rw-r--r--docs/dev.md51
-rw-r--r--examples/blog/snowpack.config.js3
-rw-r--r--examples/kitchen-sink/snowpack.config.js3
-rw-r--r--examples/snowpack/package-lock.json1
-rw-r--r--examples/tailwindcss/snowpack.config.js3
-rw-r--r--package.json2
-rw-r--r--src/dev.ts18
-rw-r--r--src/frontend/500.astro128
-rw-r--r--src/runtime.ts9
-rw-r--r--src/search.ts18
12 files changed, 233 insertions, 15 deletions
diff --git a/README.md b/README.md
index eb55208eb..7b3c6257e 100644
--- a/README.md
+++ b/README.md
@@ -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;
-}