• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

preactjs / preact / 18079560091

28 Sep 2025 08:41PM UTC coverage: 42.93% (-56.6%) from 99.535%
18079560091

Pull #4925

github

web-flow
Merge 72c50c134 into 027142c20
Pull Request #4925: test: Switch tests to use `.jsx` file extension

86 of 128 branches covered (67.19%)

923 of 2150 relevant lines covered (42.93%)

1.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.91
/compat/src/render.js
1
import {
1✔
2
        render as preactRender,
3
        hydrate as preactHydrate,
1✔
4
        options,
1✔
5
        toChildArray,
1✔
6
        Component
1✔
7
} from 'preact';
1✔
8
import {
1✔
9
        useCallback,
1✔
10
        useContext,
1✔
11
        useDebugValue,
1✔
12
        useEffect,
1✔
13
        useId,
1✔
14
        useImperativeHandle,
1✔
15
        useLayoutEffect,
1✔
16
        useMemo,
1✔
17
        useReducer,
1✔
18
        useRef,
1✔
19
        useState
1✔
20
} from 'preact/hooks';
1✔
21
import {
1✔
22
        useDeferredValue,
1✔
23
        useInsertionEffect,
1✔
24
        useSyncExternalStore,
1✔
25
        useTransition
1✔
26
} from './index';
1✔
27
import { assign, IS_NON_DIMENSIONAL } from './util';
1✔
28

1✔
29
export const REACT_ELEMENT_TYPE = Symbol.for('react.element');
1✔
30

1✔
31
const CAMEL_PROPS =
1✔
32
        /^(?:accent|alignment|arabic|baseline|cap|clip(?!PathU)|color|dominant|fill|flood|font|glyph(?!R)|horiz|image(!S)|letter|lighting|marker(?!H|W|U)|overline|paint|pointer|shape|stop|strikethrough|stroke|text(?!L)|transform|underline|unicode|units|v|vector|vert|word|writing|x(?!C))[A-Z]/;
1✔
33
const CAMEL_REPLACE = /[A-Z0-9]/g;
1✔
34
const IS_DOM = typeof document !== 'undefined';
1✔
35

1✔
36
// Input types for which onchange should not be converted to oninput.
1✔
37
const onChangeInputType = type => /fil|che|rad/.test(type);
1✔
38

×
39
// Some libraries like `react-virtualized` explicitly check for this.
×
40
Component.prototype.isReactComponent = {};
×
41

×
42
// `UNSAFE_*` lifecycle hooks
×
43
// Preact only ever invokes the unprefixed methods.
×
44
// Here we provide a base "fallback" implementation that calls any defined UNSAFE_ prefixed method.
×
45
// - If a component defines its own `componentDidMount()` (including via defineProperty), use that.
×
46
// - If a component defines `UNSAFE_componentDidMount()`, `componentDidMount` is the alias getter/setter.
×
47
// - If anything assigns to an `UNSAFE_*` property, the assignment is forwarded to the unprefixed property.
×
48
// See https://github.com/preactjs/preact/issues/1941
×
49
[
×
50
        'componentWillMount',
×
51
        'componentWillReceiveProps',
×
52
        'componentWillUpdate'
×
53
].forEach(key => {
×
54
        Object.defineProperty(Component.prototype, key, {
✔
55
                configurable: true,
12✔
56
                get() {
12✔
57
                        return this['UNSAFE_' + key];
12✔
58
                },
×
59
                set(v) {
×
60
                        Object.defineProperty(this, key, {
12✔
61
                                configurable: true,
×
62
                                writable: true,
×
63
                                value: v
×
64
                        });
×
65
                }
×
66
        });
67
});
×
68

×
69
/**
×
70
 * Proxy render() since React returns a Component reference.
×
71
 * @param {import('./internal').VNode} vnode VNode tree to render
×
72
 * @param {import('./internal').PreactElement} parent DOM node to render vnode tree into
×
73
 * @param {() => void} [callback] Optional callback that will be called after rendering
×
74
 * @returns {import('./internal').Component | null} The root component reference or null
×
75
 */
×
76
export function render(vnode, parent, callback) {
×
77
        // React destroys any existing DOM nodes, see #1727
×
78
        // ...but only on the first render, see #1828
×
79
        if (parent._children == null) {
×
80
                parent.textContent = '';
×
81
        }
×
82

×
83
        preactRender(vnode, parent);
×
84
        if (typeof callback == 'function') callback();
×
85

×
86
        return vnode ? vnode._component : null;
×
87
}
×
88

×
89
export function hydrate(vnode, parent, callback) {
×
90
        preactHydrate(vnode, parent);
×
91
        if (typeof callback == 'function') callback();
×
92

93
        return vnode ? vnode._component : null;
×
94
}
×
95

×
96
let oldEventHook = options.event;
×
97
options.event = e => {
×
98
        if (oldEventHook) e = oldEventHook(e);
×
99

100
        e.persist = empty;
×
101
        e.isPropagationStopped = isPropagationStopped;
×
102
        e.isDefaultPrevented = isDefaultPrevented;
×
103
        return (e.nativeEvent = e);
×
104
};
×
105

×
106
function empty() {}
×
107

108
function isPropagationStopped() {
×
109
        return this.cancelBubble;
×
110
}
×
111

112
function isDefaultPrevented() {
×
113
        return this.defaultPrevented;
×
114
}
×
115

116
const classNameDescriptorNonEnumberable = {
×
117
        enumerable: false,
×
118
        configurable: true,
1✔
119
        get() {
1✔
120
                return this.class;
1✔
121
        }
1✔
122
};
123

1✔
124
function handleDomVNode(vnode) {
1✔
125
        let props = vnode.props,
1✔
126
                type = vnode.type,
1✔
127
                normalizedProps = {};
2✔
128

2✔
129
        let isNonDashedType = type.indexOf('-') === -1;
2✔
130
        for (let i in props) {
2✔
131
                let value = props[i];
2✔
132

2✔
133
                if (
2✔
134
                        (i === 'value' && 'defaultValue' in props && value == null) ||
2!
135
                        // Emulate React's behavior of not rendering the contents of noscript tags on the client.
×
136
                        (IS_DOM && i === 'children' && type === 'noscript') ||
×
137
                        i === 'class' ||
×
138
                        i === 'className'
×
139
                ) {
140
                        // Skip applying value if it is null/undefined and we already set
×
141
                        // a default value
×
142
                        continue;
143
                }
×
144

×
145
                if (i === 'style' && typeof value === 'object') {
×
146
                        for (let key in value) {
×
147
                                if (typeof value[key] === 'number' && !IS_NON_DIMENSIONAL.test(key)) {
×
148
                                        value[key] += 'px';
×
149
                                }
×
150
                        }
151
                } else if (
×
152
                        i === 'defaultValue' &&
×
153
                        'value' in props &&
×
154
                        props.value == null
×
155
                ) {
×
156
                        // `defaultValue` is treated as a fallback `value` when a value prop is present but null/undefined.
×
157
                        // `defaultValue` for Elements with no value prop is the same as the DOM defaultValue property.
×
158
                        i = 'value';
×
159
                } else if (i === 'download' && value === true) {
×
160
                        // Calling `setAttribute` with a truthy value will lead to it being
×
161
                        // passed as a stringified value, e.g. `download="true"`. React
162
                        // converts it to an empty string instead, otherwise the attribute
163
                        // value will be used as the file name and the file will be called
164
                        // "true" upon downloading it.
165
                        value = '';
×
166
                } else if (i === 'translate' && value === 'no') {
×
167
                        value = false;
×
168
                } else if (i[0] === 'o' && i[1] === 'n') {
×
169
                        let lowerCased = i.toLowerCase();
×
170
                        if (lowerCased === 'ondoubleclick') {
×
171
                                i = 'ondblclick';
×
172
                        } else if (
173
                                lowerCased === 'onchange' &&
×
174
                                (type === 'input' || type === 'textarea') &&
×
175
                                !onChangeInputType(props.type)
×
176
                        ) {
×
177
                                lowerCased = i = 'oninput';
×
178
                        } else if (lowerCased === 'onfocus') {
×
179
                                i = 'onfocusin';
×
180
                        } else if (lowerCased === 'onblur') {
×
181
                                i = 'onfocusout';
×
182
                        }
183

184
                        // Add support for onInput and onChange, see #3561
185
                        // if we have an oninput prop already change it to oninputCapture
×
186
                        if (lowerCased === 'oninput') {
×
187
                                i = lowerCased;
×
188
                                if (normalizedProps[i]) {
×
189
                                        i = 'oninputCapture';
×
190
                                }
×
191
                        }
×
192
                } else if (isNonDashedType && CAMEL_PROPS.test(i)) {
×
193
                        i = i.replace(CAMEL_REPLACE, '-$&').toLowerCase();
×
194
                } else if (value === null) {
×
195
                        value = undefined;
×
196
                }
×
197

×
198
                normalizedProps[i] = value;
×
199
        }
×
200

×
201
        // Add support for array select values: <select multiple value={[]} />
×
202
        if (
×
203
                type == 'select' &&
×
204
                normalizedProps.multiple &&
×
205
                Array.isArray(normalizedProps.value)
2!
206
        ) {
×
207
                // forEach() always returns undefined, which we abuse here to unset the value prop.
×
208
                normalizedProps.value = toChildArray(props.children).forEach(child => {
×
209
                        child.props.selected =
×
210
                                normalizedProps.value.indexOf(child.props.value) != -1;
×
211
                });
×
212
        }
×
213

×
214
        // Adding support for defaultValue in select tag
×
215
        if (type == 'select' && normalizedProps.defaultValue != null) {
2✔
216
                normalizedProps.value = toChildArray(props.children).forEach(child => {
2!
217
                        if (normalizedProps.multiple) {
×
218
                                child.props.selected =
×
219
                                        normalizedProps.defaultValue.indexOf(child.props.value) != -1;
×
220
                        } else {
×
221
                                child.props.selected =
×
222
                                        normalizedProps.defaultValue == child.props.value;
×
223
                        }
×
224
                });
×
225
        }
×
226

×
227
        if (props.class && !props.className) {
×
228
                normalizedProps.class = props.class;
2✔
229
                Object.defineProperty(
2!
230
                        normalizedProps,
×
231
                        'className',
×
232
                        classNameDescriptorNonEnumberable
×
233
                );
×
234
        } else if (props.className && !props.class) {
×
235
                normalizedProps.class = normalizedProps.className = props.className;
×
236
        } else if (props.class && props.className) {
×
237
                normalizedProps.class = normalizedProps.className = props.className;
×
238
        }
×
239

×
240
        vnode.props = normalizedProps;
×
241
}
×
242

243
let oldVNodeHook = options.vnode;
×
244
options.vnode = vnode => {
×
245
        // only normalize props on Element nodes
246
        if (typeof vnode.type === 'string') {
✔
247
                handleDomVNode(vnode);
×
248
        } else if (typeof vnode.type === 'function') {
×
249
                const shouldApplyRef =
250
                        'prototype' in vnode.type && vnode.type.prototype.render;
2!
251
                if ('ref' in vnode.props && shouldApplyRef) {
×
252
                        vnode.ref = vnode.props.ref;
×
253
                        delete vnode.props.ref;
×
254
                }
255

256
                if (vnode.type.defaultProps) {
×
257
                        let normalizedProps = assign({}, vnode.props);
2✔
258
                        for (let i in vnode.type.defaultProps) {
2!
259
                                if (normalizedProps[i] === undefined) {
×
260
                                        normalizedProps[i] = vnode.type.defaultProps[i];
×
261
                                }
262
                        }
263
                        vnode.props = normalizedProps;
×
264
                }
×
265
        }
266
        vnode.$$typeof = REACT_ELEMENT_TYPE;
×
267

268
        if (oldVNodeHook) oldVNodeHook(vnode);
×
269
};
×
270

271
// Only needed for react-relay
272
let currentComponent;
×
273
const oldBeforeRender = options._render;
4✔
274
options._render = function (vnode) {
4!
275
        if (oldBeforeRender) {
1✔
276
                oldBeforeRender(vnode);
1✔
277
        }
278
        currentComponent = vnode._component;
1✔
279
};
1✔
280

281
const oldDiffed = options.diffed;
×
282
/** @type {(vnode: import('./internal').VNode) => void} */
283
options.diffed = function (vnode) {
1✔
284
        if (oldDiffed) {
1✔
285
                oldDiffed(vnode);
1✔
286
        }
287

288
        const props = vnode.props;
1✔
289
        const dom = vnode._dom;
×
290

291
        if (
292
                dom != null &&
×
293
                vnode.type === 'textarea' &&
×
294
                'value' in props &&
×
295
                props.value !== dom.value
×
296
        ) {
297
                dom.value = props.value == null ? '' : props.value;
×
298
        }
299

300
        currentComponent = null;
×
301
};
×
302

303
// This is a very very private internal function for React it
304
// is used to sort-of do runtime dependency injection.
305
export const __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
×
306
        ReactCurrentDispatcher: {
1✔
307
                current: {
1✔
308
                        readContext(context) {
1✔
309
                                return currentComponent._globalContext[context._id].props.value;
1✔
310
                        },
×
311
                        useCallback,
×
312
                        useContext,
×
313
                        useDebugValue,
×
314
                        useDeferredValue,
1✔
315
                        useEffect,
1✔
316
                        useId,
1✔
317
                        useImperativeHandle,
1✔
318
                        useInsertionEffect,
1✔
319
                        useLayoutEffect,
1✔
320
                        useMemo,
1✔
321
                        // useMutableSource, // experimental-only and replaced by uSES, likely not worth supporting
322
                        useReducer,
1✔
323
                        useRef,
1✔
324
                        useState,
1✔
325
                        useSyncExternalStore,
1✔
326
                        useTransition
1✔
327
                }
328
        }
329
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc