From e28d5cb9dededa8e8eb42598b48f43b99b05dd2c Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 29 Jun 2021 11:37:36 -0500 Subject: [PATCH] Allow `.astro` files to `throw new Error()` (#572) * fix(#526): enable `throwExpressions` * chore: add test for throwing inside of `.astro` * fix: improve build error handling * chore: add test when throwing on `build` * chore: fix changeset bot * chore: add changeset --- .changeset/nervous-pillows-sing.md | 5 ++++ packages/astro/src/build.ts | 29 +++++++++++++++---- packages/astro/src/build/page.ts | 8 ++++- packages/astro/src/compiler/codegen/index.ts | 2 +- packages/astro/test/astro-throw.test.js | 28 ++++++++++++++++++ .../fixtures/astro-throw/snowpack.config.json | 3 ++ .../astro-throw/src/pages/index.astro | 13 +++++++++ 7 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 .changeset/nervous-pillows-sing.md create mode 100644 packages/astro/test/astro-throw.test.js create mode 100644 packages/astro/test/fixtures/astro-throw/snowpack.config.json create mode 100644 packages/astro/test/fixtures/astro-throw/src/pages/index.astro diff --git a/.changeset/nervous-pillows-sing.md b/.changeset/nervous-pillows-sing.md new file mode 100644 index 000000000..851749d0a --- /dev/null +++ b/.changeset/nervous-pillows-sing.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Improve error handling within `.astro` files (#526) diff --git a/packages/astro/src/build.ts b/packages/astro/src/build.ts index fbe990169..81d86ddb4 100644 --- a/packages/astro/src/build.ts +++ b/packages/astro/src/build.ts @@ -9,7 +9,7 @@ import { performance } from 'perf_hooks'; import eslexer from 'es-module-lexer'; import cheerio from 'cheerio'; import del from 'del'; -import { bold, green, yellow } from 'kleur/colors'; +import { bold, green, yellow, red, dim, underline } from 'kleur/colors'; import mime from 'mime'; import glob from 'tiny-glob'; import { bundleCSS } from './build/bundle/css.js'; @@ -71,10 +71,11 @@ export async function build(astroConfig: AstroConfig, logging: LogOptions = defa timer.build = performance.now(); const pages = await allPages(pagesRoot); info(logging, 'build', yellow('! building pages...')); - await Promise.all( - pages.map(async (filepath) => { + try { + await Promise.all( + pages.map((filepath) => { const buildPage = getPageType(filepath) === 'collection' ? buildCollectionPage : buildStaticPage; - await buildPage({ + return buildPage({ astroConfig, buildState, filepath, @@ -85,7 +86,25 @@ export async function build(astroConfig: AstroConfig, logging: LogOptions = defa site: astroConfig.buildOptions.site, }); }) - ); + ) + } catch (e) { + if (e.filename) { + let stack = e.stack.replace(/Object\.__render \(/gm, '').replace(/\/_astro\/(.+)\.astro\.js\:\d+\:\d+\)/gm, (_: string, $1: string) => 'file://' + fileURLToPath(projectRoot) + $1 + '.astro').split('\n'); + stack.splice(1, 0, ` at file://${e.filename}`) + stack = stack.join('\n') + + error(logging, 'build', `${red(`Unable to render ${underline(e.filename.replace(fileURLToPath(projectRoot), ''))}`)} + +${stack} +`); + } else { + error(logging, 'build', e); + } + error(logging, 'build', red('✕ building pages failed!')); + + await runtime.shutdown(); + return 1; + } info(logging, 'build', green('✔'), 'pages built.'); debug(logging, 'build', `built pages [${stopTimer(timer.build)}]`); diff --git a/packages/astro/src/build/page.ts b/packages/astro/src/build/page.ts index e19cf097b..d7e8e276c 100644 --- a/packages/astro/src/build/page.ts +++ b/packages/astro/src/build/page.ts @@ -3,6 +3,7 @@ import type { AstroRuntime, LoadResult } from '../runtime'; import type { LogOptions } from '../logger'; import path from 'path'; import { generateRSS } from './rss.js'; +import { fileURLToPath } from 'url'; interface PageBuildOptions { astroConfig: AstroConfig; @@ -92,7 +93,12 @@ export async function buildStaticPage({ astroConfig, buildState, filepath, runti const { pages: pagesRoot } = astroConfig; const url = filepath.pathname.replace(pagesRoot.pathname, '/').replace(/(index)?\.(astro|md)$/, ''); const result = await runtime.load(url); - if (result.statusCode !== 200) throw new Error((result as any).error); + if (result.statusCode !== 200) { + let err = (result as any).error; + if (!(err instanceof Error)) err = new Error(err); + err.filename = fileURLToPath(filepath); + throw err; + } const outFile = path.posix.join(url, '/index.html'); buildState[outFile] = { srcPath: filepath, diff --git a/packages/astro/src/compiler/codegen/index.ts b/packages/astro/src/compiler/codegen/index.ts index 8e1edf763..843ecc428 100644 --- a/packages/astro/src/compiler/codegen/index.ts +++ b/packages/astro/src/compiler/codegen/index.ts @@ -277,7 +277,7 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp if (module) { const parseOptions: babelParser.ParserOptions = { sourceType: 'module', - plugins: ['jsx', 'typescript', 'topLevelAwait'], + plugins: ['jsx', 'typescript', 'topLevelAwait', 'throwExpressions'], }; let parseResult; try { diff --git a/packages/astro/test/astro-throw.test.js b/packages/astro/test/astro-throw.test.js new file mode 100644 index 000000000..702828187 --- /dev/null +++ b/packages/astro/test/astro-throw.test.js @@ -0,0 +1,28 @@ +import { suite } from 'uvu'; +import * as assert from 'uvu/assert'; +import { doc } from './test-utils.js'; +import { setup, setupBuild } from './helpers.js'; + +const Throwable = suite('Throw test'); + +setup(Throwable, './fixtures/astro-throw', { + runtimeOptions: { + mode: 'development', + }, +}); +setupBuild(Throwable, './fixtures/astro-throw'); + +Throwable('Can throw an error from an `.astro` file', async ({ runtime }) => { + const result = await runtime.load('/'); + assert.equal(result.statusCode, 500); + assert.equal(result.error.message, 'Oops!'); +}); + +Throwable('Does not complete build when Error is thrown', async ({ build }) => { + await build().catch(e => { + assert.ok(e, 'Build threw'); + }) +}); + + +Throwable.run(); diff --git a/packages/astro/test/fixtures/astro-throw/snowpack.config.json b/packages/astro/test/fixtures/astro-throw/snowpack.config.json new file mode 100644 index 000000000..8f034781d --- /dev/null +++ b/packages/astro/test/fixtures/astro-throw/snowpack.config.json @@ -0,0 +1,3 @@ +{ + "workspaceRoot": "../../../../../" +} diff --git a/packages/astro/test/fixtures/astro-throw/src/pages/index.astro b/packages/astro/test/fixtures/astro-throw/src/pages/index.astro new file mode 100644 index 000000000..69e9fcd30 --- /dev/null +++ b/packages/astro/test/fixtures/astro-throw/src/pages/index.astro @@ -0,0 +1,13 @@ +--- +let title = 'My App' + +throw new Error('Oops!') +--- + + + + + +

I will never render.

+ +