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:
parent
42a6ceb587
commit
e28d5cb9de
7 changed files with 81 additions and 7 deletions
5
.changeset/nervous-pillows-sing.md
Normal file
5
.changeset/nervous-pillows-sing.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Improve error handling within `.astro` files (#526)
|
|
@ -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)}]`);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
28
packages/astro/test/astro-throw.test.js
Normal file
28
packages/astro/test/astro-throw.test.js
Normal 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();
|
3
packages/astro/test/fixtures/astro-throw/snowpack.config.json
vendored
Normal file
3
packages/astro/test/fixtures/astro-throw/snowpack.config.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"workspaceRoot": "../../../../../"
|
||||
}
|
13
packages/astro/test/fixtures/astro-throw/src/pages/index.astro
vendored
Normal file
13
packages/astro/test/fixtures/astro-throw/src/pages/index.astro
vendored
Normal 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>
|
Loading…
Reference in a new issue