Skip to content

Commit 7adb350

Browse files
authoredAug 9, 2024··
Prevent throwing in react and solid component checks (#11624)
1 parent cc405dd commit 7adb350

File tree

7 files changed

+36
-53
lines changed

7 files changed

+36
-53
lines changed
 

‎.changeset/few-starfishes-change.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/solid-js': patch
3+
---
4+
5+
Prevents throwing errors when checking if a component is a Solid component in runtime

‎.changeset/spotty-lies-eat.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/react': patch
3+
---
4+
5+
Prevents throwing errors when checking if a component is a React component in runtime

‎packages/astro/e2e/errors.test.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,18 @@ test.describe('Error display', () => {
6767
expect(fileLocation).toMatch(/^pages\/import-not-found\.astro/);
6868
});
6969

70+
// NOTE: It's not possible to detect some JSX components if they have errors because
71+
// their renderers' check functions run the render directly, and if a runtime error is
72+
// thrown, it assumes that it's simply not that renderer's component and skips it
7073
test('shows correct file path when a component has an error', async ({ page, astro }) => {
71-
await page.goto(astro.resolveUrl('/preact-runtime-error'), { waitUntil: 'networkidle' });
74+
await page.goto(astro.resolveUrl('/vue-runtime-error'), { waitUntil: 'networkidle' });
7275

7376
const { fileLocation, absoluteFileLocation } = await getErrorOverlayContent(page);
7477
const absoluteFileUrl = 'file://' + absoluteFileLocation.replace(/:\d+:\d+$/, '');
7578
const fileExists = astro.pathExists(absoluteFileUrl);
7679

7780
expect(fileExists).toBeTruthy();
78-
expect(fileLocation).toMatch(/^preact\/PreactRuntimeError.jsx/);
81+
expect(fileLocation).toMatch(/^vue\/VueRuntimeError.vue/);
7982
});
8083

8184
test('shows correct line when a style preprocess has an error', async ({ page, astro }) => {

‎packages/integrations/preact/src/server.ts

+9-11
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,17 @@ async function check(
2727
useConsoleFilter();
2828

2929
try {
30-
try {
31-
const { html } = await renderToStaticMarkup.call(this, Component, props, children, undefined);
32-
if (typeof html !== 'string') {
33-
return false;
34-
}
35-
36-
// There are edge cases (SolidJS) where Preact *might* render a string,
37-
// but components would be <undefined></undefined>
38-
// It also might render an empty sting.
39-
return html == '' ? false : !html.includes('<undefined>');
40-
} catch {
30+
const { html } = await renderToStaticMarkup.call(this, Component, props, children, undefined);
31+
if (typeof html !== 'string') {
4132
return false;
4233
}
34+
35+
// There are edge cases (SolidJS) where Preact *might* render a string,
36+
// but components would be <undefined></undefined>
37+
// It also might render an empty sting.
38+
return html == '' ? false : !html.includes('<undefined>');
39+
} catch {
40+
return false;
4341
} finally {
4442
finishUsingConsoleFilter();
4543
}

‎packages/integrations/react/server-v17.js

+1-17
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@ import StaticHtml from './static-html.js';
55
const slotName = (str) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());
66
const reactTypeof = Symbol.for('react.element');
77

8-
function errorIsComingFromPreactComponent(err) {
9-
return (
10-
err.message &&
11-
(err.message.startsWith("Cannot read property '__H'") ||
12-
err.message.includes("(reading '__H')"))
13-
);
14-
}
15-
168
function check(Component, props, children) {
179
// Note: there are packages that do some unholy things to create "components".
1810
// Checking the $$typeof property catches most of these patterns.
@@ -26,28 +18,20 @@ function check(Component, props, children) {
2618
return React.Component.isPrototypeOf(Component) || React.PureComponent.isPrototypeOf(Component);
2719
}
2820

29-
let error = null;
3021
let isReactComponent = false;
3122
function Tester(...args) {
3223
try {
3324
const vnode = Component(...args);
3425
if (vnode && vnode['$$typeof'] === reactTypeof) {
3526
isReactComponent = true;
3627
}
37-
} catch (err) {
38-
if (!errorIsComingFromPreactComponent(err)) {
39-
error = err;
40-
}
41-
}
28+
} catch {}
4229

4330
return React.createElement('div');
4431
}
4532

4633
renderToStaticMarkup(Tester, props, children, {});
4734

48-
if (error) {
49-
throw error;
50-
}
5135
return isReactComponent;
5236
}
5337

‎packages/integrations/react/server.js

+1-17
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,6 @@ import StaticHtml from './static-html.js';
77
const slotName = (str) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());
88
const reactTypeof = Symbol.for('react.element');
99

10-
function errorIsComingFromPreactComponent(err) {
11-
return (
12-
err.message &&
13-
(err.message.startsWith("Cannot read property '__H'") ||
14-
err.message.includes("(reading '__H')"))
15-
);
16-
}
17-
1810
async function check(Component, props, children) {
1911
// Note: there are packages that do some unholy things to create "components".
2012
// Checking the $$typeof property catches most of these patterns.
@@ -32,28 +24,20 @@ async function check(Component, props, children) {
3224
return React.Component.isPrototypeOf(Component) || React.PureComponent.isPrototypeOf(Component);
3325
}
3426

35-
let error = null;
3627
let isReactComponent = false;
3728
function Tester(...args) {
3829
try {
3930
const vnode = Component(...args);
4031
if (vnode && vnode['$$typeof'] === reactTypeof) {
4132
isReactComponent = true;
4233
}
43-
} catch (err) {
44-
if (!errorIsComingFromPreactComponent(err)) {
45-
error = err;
46-
}
47-
}
34+
} catch {}
4835

4936
return React.createElement('div');
5037
}
5138

5239
await renderToStaticMarkup(Tester, props, children, {});
5340

54-
if (error) {
55-
throw error;
56-
}
5741
return isReactComponent;
5842
}
5943

‎packages/integrations/solid/src/server.ts

+10-6
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,16 @@ async function check(
2828
// In general, components from other frameworks (eg, MDX, React, etc.) tend to render as "undefined",
2929
// so we take advantage of this trick to decide if this is a Solid component or not.
3030

31-
const { html } = await renderToStaticMarkup.call(this, Component, props, children, {
32-
// The purpose of check() is just to validate that this is a Solid component and not
33-
// React, etc. We should render in sync mode which should skip Suspense boundaries
34-
// or loading resources like external API calls.
35-
renderStrategy: 'sync' as RenderStrategy,
36-
});
31+
let html: string | undefined;
32+
try {
33+
const result = await renderToStaticMarkup.call(this, Component, props, children, {
34+
// The purpose of check() is just to validate that this is a Solid component and not
35+
// React, etc. We should render in sync mode which should skip Suspense boundaries
36+
// or loading resources like external API calls.
37+
renderStrategy: 'sync' as RenderStrategy,
38+
});
39+
html = result.html;
40+
} catch {}
3741

3842
return typeof html === 'string';
3943
}

0 commit comments

Comments
 (0)
Please sign in to comment.