Compare commits
4 commits
main
...
mdx-throw-
Author | SHA1 | Date | |
---|---|---|---|
|
e540b6b68a | ||
|
410cfe306c | ||
|
f67f18af7a | ||
|
25643a0beb |
9 changed files with 120 additions and 15 deletions
5
.changeset/hungry-tomatoes-cough.md
Normal file
5
.changeset/hungry-tomatoes-cough.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Prevent jsx throws from hanging server
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import { SSRResult } from '../../@types/astro.js';
|
import { SSRResult } from '../../@types/astro.js';
|
||||||
import { AstroJSX, isVNode } from '../../jsx-runtime/index.js';
|
import { AstroJSX, AstroVNode, isVNode } from '../../jsx-runtime/index.js';
|
||||||
import {
|
import {
|
||||||
escapeHTML,
|
escapeHTML,
|
||||||
HTMLString,
|
HTMLString,
|
||||||
|
@ -15,7 +15,24 @@ import type { ComponentIterable } from './render/component';
|
||||||
|
|
||||||
const ClientOnlyPlaceholder = 'astro-client-only';
|
const ClientOnlyPlaceholder = 'astro-client-only';
|
||||||
|
|
||||||
const skipAstroJSXCheck = new WeakSet();
|
const skipAstroJSXCheck = new WeakMap<() => any, number>();
|
||||||
|
function addSkip(vnode: AstroVNode) {
|
||||||
|
if(typeof vnode.type === 'function') {
|
||||||
|
skipAstroJSXCheck.set(vnode.type, skipCount(vnode) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function skipCount(vnode: AstroVNode): number {
|
||||||
|
if(typeof vnode.type === 'function') {
|
||||||
|
return skipAstroJSXCheck.get(vnode.type) || 0;
|
||||||
|
}
|
||||||
|
return NaN;
|
||||||
|
}
|
||||||
|
function deleteSkips(vnode: AstroVNode) {
|
||||||
|
if(typeof vnode.type === 'function') {
|
||||||
|
skipAstroJSXCheck.delete(vnode.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let originalConsoleError: any;
|
let originalConsoleError: any;
|
||||||
let consoleFilterRefs = 0;
|
let consoleFilterRefs = 0;
|
||||||
|
|
||||||
|
@ -68,25 +85,35 @@ Did you forget to import the component or is it possible there is a typo?`);
|
||||||
|
|
||||||
if (vnode.type) {
|
if (vnode.type) {
|
||||||
if (typeof vnode.type === 'function' && (vnode.type as any)['astro:renderer']) {
|
if (typeof vnode.type === 'function' && (vnode.type as any)['astro:renderer']) {
|
||||||
skipAstroJSXCheck.add(vnode.type);
|
addSkip(vnode);
|
||||||
}
|
}
|
||||||
if (typeof vnode.type === 'function' && vnode.props['server:root']) {
|
if (typeof vnode.type === 'function' && vnode.props['server:root']) {
|
||||||
const output = await vnode.type(vnode.props ?? {});
|
const output = await vnode.type(vnode.props ?? {});
|
||||||
return await renderJSX(result, output);
|
return await renderJSX(result, output);
|
||||||
}
|
}
|
||||||
if (typeof vnode.type === 'function' && !skipAstroJSXCheck.has(vnode.type)) {
|
if (typeof vnode.type === 'function') {
|
||||||
useConsoleFilter();
|
if(skipCount(vnode) === 0 || skipCount(vnode) > 2) {
|
||||||
try {
|
useConsoleFilter();
|
||||||
const output = await vnode.type(vnode.props ?? {});
|
try {
|
||||||
if (output && output[AstroJSX]) {
|
const output = await vnode.type(vnode.props ?? {});
|
||||||
return await renderJSX(result, output);
|
let renderResult: any;
|
||||||
} else if (!output) {
|
if (output && output[AstroJSX]) {
|
||||||
return await renderJSX(result, output);
|
renderResult = await renderJSX(result, output);
|
||||||
|
} else if (!output) {
|
||||||
|
renderResult = await renderJSX(result, output);
|
||||||
|
}
|
||||||
|
deleteSkips(vnode);
|
||||||
|
return renderResult;
|
||||||
|
} catch (e: any) {
|
||||||
|
if(skipCount(vnode) > 2) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
addSkip(vnode);
|
||||||
|
} finally {
|
||||||
|
finishUsingConsoleFilter();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} else {
|
||||||
skipAstroJSXCheck.add(vnode.type);
|
addSkip(vnode);
|
||||||
} finally {
|
|
||||||
finishUsingConsoleFilter();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,8 +178,10 @@ Did you forget to import the component or is it possible there is a typo?`);
|
||||||
for await (const chunk of output) {
|
for await (const chunk of output) {
|
||||||
parts.append(chunk, result);
|
parts.append(chunk, result);
|
||||||
}
|
}
|
||||||
|
deleteSkips(vnode);
|
||||||
return markHTMLString(parts.toString());
|
return markHTMLString(parts.toString());
|
||||||
} else {
|
} else {
|
||||||
|
deleteSkips(vnode);
|
||||||
return markHTMLString(output);
|
return markHTMLString(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
5
packages/integrations/mdx/test/fixtures/mdx-throw-error/astro.config.mjs
vendored
Normal file
5
packages/integrations/mdx/test/fixtures/mdx-throw-error/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
integrations: [mdx()]
|
||||||
|
}
|
7
packages/integrations/mdx/test/fixtures/mdx-throw-error/package.json
vendored
Normal file
7
packages/integrations/mdx/test/fixtures/mdx-throw-error/package.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"name": "@test/mdx-throw-error",
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "workspace:*",
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
5
packages/integrations/mdx/test/fixtures/mdx-throw-error/src/components/component.mdx
vendored
Normal file
5
packages/integrations/mdx/test/fixtures/mdx-throw-error/src/components/component.mdx
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { throwError } from '../js/throwerror';
|
||||||
|
|
||||||
|
This will probably result in a stack overflow. :(
|
||||||
|
|
||||||
|
<pre>{ throwError() }</pre>
|
5
packages/integrations/mdx/test/fixtures/mdx-throw-error/src/js/throwerror.ts
vendored
Normal file
5
packages/integrations/mdx/test/fixtures/mdx-throw-error/src/js/throwerror.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export function throwError() {
|
||||||
|
console.log(`I'm going to throw an error. The server will just keep spinning until it runs out of memory...`);
|
||||||
|
|
||||||
|
throw new Error('Oh no');
|
||||||
|
}
|
16
packages/integrations/mdx/test/fixtures/mdx-throw-error/src/pages/index.astro
vendored
Normal file
16
packages/integrations/mdx/test/fixtures/mdx-throw-error/src/pages/index.astro
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
|
||||||
|
import Component from '../components/component.mdx';
|
||||||
|
---
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Testing</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>Astro MDX Errors</h1>
|
||||||
|
|
||||||
|
<Component />
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
packages/integrations/mdx/test/mdx-throw-errors.test.js
Normal file
25
packages/integrations/mdx/test/mdx-throw-errors.test.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
|
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { loadFixture } from '../../../astro/test/test-utils.js';
|
||||||
|
|
||||||
|
describe('MDX errors', () => {
|
||||||
|
/** @type {import('../../../astro/test/test-utils.js').Fixture} */
|
||||||
|
let fixture;
|
||||||
|
|
||||||
|
before(async () => {
|
||||||
|
fixture = await loadFixture({
|
||||||
|
root: new URL('./fixtures/mdx-throw-error/', import.meta.url),
|
||||||
|
integrations: [mdx()],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error during the build (does not lock up)', async () => {
|
||||||
|
try {
|
||||||
|
await fixture.build();
|
||||||
|
expect(false).to.equal(true);
|
||||||
|
} catch(err) {
|
||||||
|
expect(err.message).to.equal('Oh no');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
|
@ -2821,6 +2821,14 @@ importers:
|
||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
react-dom: 18.2.0_react@18.2.0
|
react-dom: 18.2.0_react@18.2.0
|
||||||
|
|
||||||
|
packages/integrations/mdx/test/fixtures/mdx-throw-error:
|
||||||
|
specifiers:
|
||||||
|
'@astrojs/mdx': workspace:*
|
||||||
|
astro: workspace:*
|
||||||
|
dependencies:
|
||||||
|
'@astrojs/mdx': link:../../..
|
||||||
|
astro: link:../../../../../astro
|
||||||
|
|
||||||
packages/integrations/mdx/test/fixtures/mdx-vite-env-vars:
|
packages/integrations/mdx/test/fixtures/mdx-vite-env-vars:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@astrojs/mdx': workspace:*
|
'@astrojs/mdx': workspace:*
|
||||||
|
|
Loading…
Add table
Reference in a new issue