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 eslexer from 'es-module-lexer';
|
||||||
import cheerio from 'cheerio';
|
import cheerio from 'cheerio';
|
||||||
import del from 'del';
|
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 mime from 'mime';
|
||||||
import glob from 'tiny-glob';
|
import glob from 'tiny-glob';
|
||||||
import { bundleCSS } from './build/bundle/css.js';
|
import { bundleCSS } from './build/bundle/css.js';
|
||||||
|
@ -71,10 +71,11 @@ export async function build(astroConfig: AstroConfig, logging: LogOptions = defa
|
||||||
timer.build = performance.now();
|
timer.build = performance.now();
|
||||||
const pages = await allPages(pagesRoot);
|
const pages = await allPages(pagesRoot);
|
||||||
info(logging, 'build', yellow('! building pages...'));
|
info(logging, 'build', yellow('! building pages...'));
|
||||||
await Promise.all(
|
try {
|
||||||
pages.map(async (filepath) => {
|
await Promise.all(
|
||||||
|
pages.map((filepath) => {
|
||||||
const buildPage = getPageType(filepath) === 'collection' ? buildCollectionPage : buildStaticPage;
|
const buildPage = getPageType(filepath) === 'collection' ? buildCollectionPage : buildStaticPage;
|
||||||
await buildPage({
|
return buildPage({
|
||||||
astroConfig,
|
astroConfig,
|
||||||
buildState,
|
buildState,
|
||||||
filepath,
|
filepath,
|
||||||
|
@ -85,7 +86,25 @@ export async function build(astroConfig: AstroConfig, logging: LogOptions = defa
|
||||||
site: astroConfig.buildOptions.site,
|
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.');
|
info(logging, 'build', green('✔'), 'pages built.');
|
||||||
debug(logging, 'build', `built pages [${stopTimer(timer.build)}]`);
|
debug(logging, 'build', `built pages [${stopTimer(timer.build)}]`);
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { AstroRuntime, LoadResult } from '../runtime';
|
||||||
import type { LogOptions } from '../logger';
|
import type { LogOptions } from '../logger';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { generateRSS } from './rss.js';
|
import { generateRSS } from './rss.js';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
interface PageBuildOptions {
|
interface PageBuildOptions {
|
||||||
astroConfig: AstroConfig;
|
astroConfig: AstroConfig;
|
||||||
|
@ -92,7 +93,12 @@ export async function buildStaticPage({ astroConfig, buildState, filepath, runti
|
||||||
const { pages: pagesRoot } = astroConfig;
|
const { pages: pagesRoot } = astroConfig;
|
||||||
const url = filepath.pathname.replace(pagesRoot.pathname, '/').replace(/(index)?\.(astro|md)$/, '');
|
const url = filepath.pathname.replace(pagesRoot.pathname, '/').replace(/(index)?\.(astro|md)$/, '');
|
||||||
const result = await runtime.load(url);
|
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');
|
const outFile = path.posix.join(url, '/index.html');
|
||||||
buildState[outFile] = {
|
buildState[outFile] = {
|
||||||
srcPath: filepath,
|
srcPath: filepath,
|
||||||
|
|
|
@ -277,7 +277,7 @@ function compileModule(module: Script, state: CodegenState, compileOptions: Comp
|
||||||
if (module) {
|
if (module) {
|
||||||
const parseOptions: babelParser.ParserOptions = {
|
const parseOptions: babelParser.ParserOptions = {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
plugins: ['jsx', 'typescript', 'topLevelAwait'],
|
plugins: ['jsx', 'typescript', 'topLevelAwait', 'throwExpressions'],
|
||||||
};
|
};
|
||||||
let parseResult;
|
let parseResult;
|
||||||
try {
|
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