-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
server.ts
130 lines (109 loc) · 3.77 KB
/
server.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { Component as BaseComponent, h } from 'preact';
import render from 'preact-render-to-string';
import { getContext } from './context.js';
import { restoreSignalsOnProps, serializeSignals } from './signals.js';
import StaticHtml from './static-html.js';
import type { AstroPreactAttrs, RendererContext } from './types';
const slotName = (str: string) => str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());
let originalConsoleError: typeof console.error;
let consoleFilterRefs = 0;
function check(this: RendererContext, Component: any, props: Record<string, any>, children: any) {
if (typeof Component !== 'function') return false;
if (Component.prototype != null && typeof Component.prototype.render === 'function') {
return BaseComponent.isPrototypeOf(Component);
}
useConsoleFilter();
try {
try {
const { html } = renderToStaticMarkup.call(this, Component, props, children);
if (typeof html !== 'string') {
return false;
}
// There are edge cases (SolidJS) where Preact *might* render a string,
// but components would be <undefined></undefined>
return !/\<undefined\>/.test(html);
} catch (err) {
return false;
}
} finally {
finishUsingConsoleFilter();
}
}
function renderToStaticMarkup(
this: RendererContext,
Component: any,
props: Record<string, any>,
{ default: children, ...slotted }: Record<string, any>
) {
const ctx = getContext(this.result);
const slots: Record<string, ReturnType<typeof h>> = {};
for (const [key, value] of Object.entries(slotted)) {
const name = slotName(key);
slots[name] = h(StaticHtml, { value, name });
}
// Restore signals back onto props so that they will be passed as-is to components
let propsMap = restoreSignalsOnProps(ctx, props);
const newProps = { ...props, ...slots };
const attrs: AstroPreactAttrs = {};
serializeSignals(ctx, props, attrs, propsMap);
const html = render(
h(Component, newProps, children != null ? h(StaticHtml, { value: children }) : children)
);
return {
attrs,
html,
};
}
/**
* Reduces console noise by filtering known non-problematic errors.
*
* Performs reference counting to allow parallel usage from async code.
*
* To stop filtering, please ensure that there always is a matching call
* to `finishUsingConsoleFilter` afterwards.
*/
function useConsoleFilter() {
consoleFilterRefs++;
if (!originalConsoleError) {
// eslint-disable-next-line no-console
originalConsoleError = console.error;
try {
// eslint-disable-next-line no-console
console.error = filteredConsoleError;
} catch (error) {
// If we're unable to hook `console.error`, just accept it
}
}
}
/**
* Indicates that the filter installed by `useConsoleFilter`
* is no longer needed by the calling code.
*/
function finishUsingConsoleFilter() {
consoleFilterRefs--;
// Note: Instead of reverting `console.error` back to the original
// when the reference counter reaches 0, we leave our hook installed
// to prevent potential race conditions once `check` is made async
}
/**
* Hook/wrapper function for the global `console.error` function.
*
* Ignores known non-problematic errors while any code is using the console filter.
* Otherwise, simply forwards all arguments to the original function.
*/
function filteredConsoleError(msg: string, ...rest: any[]) {
if (consoleFilterRefs > 0 && typeof msg === 'string') {
// In `check`, we attempt to render JSX components through Preact.
// When attempting this on a React component, React may output
// the following error, which we can safely filter out:
const isKnownReactHookError =
msg.includes('Warning: Invalid hook call.') &&
msg.includes('https://reactjs.org/link/invalid-hook-call');
if (isKnownReactHookError) return;
}
originalConsoleError(msg, ...rest);
}
export default {
check,
renderToStaticMarkup,
};