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
This commit is contained in:
Nate Moore 2021-06-29 11:37:36 -05:00 committed by GitHub
parent 42a6ceb587
commit e28d5cb9de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 81 additions and 7 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Improve error handling within `.astro` files (#526)

View file

@ -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)}]`);

View file

@ -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,

View file

@ -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 {

View file

@ -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();

View file

@ -0,0 +1,3 @@
{
"workspaceRoot": "../../../../../"
}

View file

@ -0,0 +1,13 @@
---
let title = 'My App'
throw new Error('Oops!')
---
<html>
<head>
</head>
<body>
<h1>I will never render.</h1>
</body>
</html>