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

infernojs / inferno / 13603195751

01 Mar 2025 09:03AM UTC coverage: 92.867% (-0.5%) from 93.357%
13603195751

push

github

Havunen
test github actions, instead of travis

1841 of 2109 branches covered (87.29%)

4049 of 4360 relevant lines covered (92.87%)

4153.88 hits per line

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

92.97
/packages/inferno-server/src/renderToString.stream.ts
1
import {
2
  isArray,
3
  isFunction,
4
  isInvalid,
5
  isNull,
6
  isNullOrUndef,
7
  isNumber,
8
  isString,
9
} from 'inferno-shared';
17✔
10
import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags';
17✔
11
import { Readable } from 'stream';
17✔
12
import { renderStyleAttribute } from './prop-renderers';
17✔
13
import {
14
  createDerivedState,
15
  escapeText,
16
  isAttributeNameSafe,
17
  renderFunctionalComponent,
18
  voidElements,
19
} from './utils';
17✔
20
import type { VNode } from 'inferno';
21
import { mergePendingState } from './stream/streamUtils';
17✔
22

23
const resolvedPromise = Promise.resolve();
17✔
24

25
export class RenderStream extends Readable {
×
26
  public initNode: any;
27
  public started: boolean = false;
28

29
  constructor(initNode) {
30
    super();
73✔
31
    this.initNode = initNode;
73✔
32
  }
33

34
  public _read(): void {
35
    if (this.started) {
73!
36
      return;
×
37
    }
38
    this.started = true;
73✔
39

40
    resolvedPromise
73✔
41
      .then(() => {
42
        return this.renderNode(this.initNode, null);
73✔
43
      })
44
      .then(() => {
45
        this.push(null);
73✔
46
      })
47
      .catch((err) => {
48
        this.emit('error', err);
×
49
      });
50
  }
51

52
  public renderNode(vNode, context) {
53
    const flags = vNode.flags;
133✔
54

55
    if ((flags & VNodeFlags.Component) > 0) {
133✔
56
      return this.renderComponent(
20✔
57
        vNode,
58
        context,
59
        flags & VNodeFlags.ComponentClass,
60
      );
61
    }
62
    if ((flags & VNodeFlags.Element) > 0) {
113✔
63
      return this.renderElement(vNode, context);
92✔
64
    }
65
    if (isArray(vNode) || (flags & VNodeFlags.Fragment) !== 0) {
21✔
66
      return this.renderArrayOrFragment(vNode, context);
5✔
67
    }
68

69
    this.renderText(vNode);
16✔
70
  }
71

72
  public renderArrayOrFragment(vNode, context) {
73
    const childFlags = vNode.childFlags;
5✔
74

75
    if (
5✔
76
      childFlags === ChildFlags.HasVNodeChildren ||
11✔
77
      (isArray(vNode) && vNode.length === 0)
78
    ) {
79
      return this.push('<!--!-->');
2✔
80
    } else if (childFlags & ChildFlags.MultipleChildren || isArray(vNode)) {
3✔
81
      const children = isArray(vNode) ? vNode : vNode.children;
3✔
82

83
      return (children as VNode[]).reduce(async (p, child) => {
3✔
84
        return await p.then(async () => {
7✔
85
          return await Promise.resolve(this.renderNode(child, context)).then(
7✔
86
            () => !!(child.flags & VNodeFlags.Text),
7✔
87
          );
88
        });
89
      }, Promise.resolve(false));
90
    }
91
  }
92

93
  public renderComponent(vComponent, context, isClass) {
94
    const type = vComponent.type;
20✔
95
    const props = vComponent.props;
20✔
96

97
    if (!isClass) {
20✔
98
      const renderOutput = renderFunctionalComponent(vComponent, context);
8✔
99

100
      if (isInvalid(renderOutput)) {
8✔
101
        return this.push('<!--!-->');
1✔
102
      }
103
      if (isString(renderOutput)) {
7✔
104
        return this.push(escapeText(renderOutput));
1✔
105
      }
106
      if (isNumber(renderOutput)) {
6✔
107
        return this.push(renderOutput + '');
1✔
108
      }
109

110
      return this.renderNode(renderOutput, context);
5✔
111
    }
112

113
    const instance = new type(props, context);
12✔
114
    const hasNewAPI = Boolean(type.getDerivedStateFromProps);
12✔
115
    instance.$BS = false;
12✔
116
    instance.$SSR = true;
12✔
117
    let childContext;
118
    if (isFunction(instance.getChildContext)) {
12✔
119
      childContext = instance.getChildContext();
1✔
120
    }
121

122
    if (!isNullOrUndef(childContext)) {
12✔
123
      context = { ...context, ...childContext };
1✔
124
    }
125
    instance.context = context;
12✔
126
    instance.$BR = true;
12✔
127

128
    return Promise.resolve(!hasNewAPI && instance.componentWillMount?.()).then(
12✔
129
      () => {
130
        mergePendingState(instance);
12✔
131

132
        if (hasNewAPI) {
12✔
133
          instance.state = createDerivedState(instance, props, instance.state);
1✔
134
        }
135
        const renderOutput = instance.render(
12✔
136
          instance.props,
137
          instance.state,
138
          instance.context,
139
        );
140

141
        if (isInvalid(renderOutput)) {
12✔
142
          return this.push('<!--!-->');
1✔
143
        }
144
        if (isString(renderOutput)) {
11✔
145
          return this.push(escapeText(renderOutput));
1✔
146
        }
147
        if (isNumber(renderOutput)) {
10✔
148
          return this.push(renderOutput + '');
1✔
149
        }
150

151
        return this.renderNode(renderOutput, context);
9✔
152
      },
153
    );
154
  }
155

156
  public renderChildren(
157
    children: VNode[] | VNode | string,
158
    context: any,
159
    childFlags: ChildFlags,
160
  ) {
161
    if (childFlags === ChildFlags.HasVNodeChildren) {
55✔
162
      return this.renderNode(children, context);
14✔
163
    }
164
    if (childFlags === ChildFlags.HasTextChildren) {
41✔
165
      return this.push(
28✔
166
        (children as string) === ''
28✔
167
          ? ' '
168
          : escapeText((children as string) + ''),
169
      );
170
    }
171
    if (childFlags & ChildFlags.MultipleChildren) {
13✔
172
      return (children as VNode[]).reduce(async (p, child) => {
13✔
173
        return await p.then(async () => {
25✔
174
          return await Promise.resolve(this.renderNode(child, context)).then(
25✔
175
            () => !!(child.flags & VNodeFlags.Text),
25✔
176
          );
177
        });
178
      }, Promise.resolve(false));
179
    }
180
  }
181

182
  public renderText(vNode): void {
183
    this.push(vNode.children === '' ? ' ' : escapeText(vNode.children));
16!
184
  }
185

186
  public renderElement(vNode, context) {
187
    const type = vNode.type;
92✔
188
    const props = vNode.props;
92✔
189
    let renderedString = `<${type}`;
92✔
190
    let html;
191
    const isVoidElement = voidElements.has(type);
92✔
192
    const className = vNode.className;
92✔
193

194
    if (isString(className)) {
92✔
195
      renderedString += ` class="${escapeText(className)}"`;
6✔
196
    } else if (isNumber(className)) {
86✔
197
      renderedString += ` class="${className}"`;
2✔
198
    }
199

200
    if (!isNull(props)) {
92✔
201
      for (const prop in props) {
33✔
202
        const value = props[prop];
30✔
203

204
        switch (prop) {
30!
205
          case 'dangerouslySetInnerHTML':
206
            html = value.__html;
1✔
207
            break;
1✔
208
          case 'style':
209
            if (!isNullOrUndef(props.style)) {
14✔
210
              renderedString += renderStyleAttribute(props.style);
12✔
211
            }
212
            break;
14✔
213
          case 'children':
214
          case 'className':
215
            // Ignore
216
            break;
×
217
          case 'defaultValue':
218
            // Use default values if normal values are not present
219
            if (!props.value) {
4✔
220
              renderedString += ` value="${
3✔
221
                isString(value) ? escapeText(value) : value
3✔
222
              }"`;
223
            }
224
            break;
4✔
225
          case 'defaultChecked':
226
            // Use default values if normal values are not present
227
            if (!props.checked) {
×
228
              renderedString += ` checked="${value}"`;
×
229
            }
230
            break;
×
231
          default:
232
            if (isAttributeNameSafe(prop)) {
11✔
233
              if (isString(value)) {
8✔
234
                renderedString += ` ${prop}="${escapeText(value)}"`;
6✔
235
              } else if (isNumber(value)) {
2!
236
                renderedString += ` ${prop}="${value}"`;
×
237
              } else if (value === true) {
2✔
238
                renderedString += ` ${prop}`;
2✔
239
              }
240
              break;
8✔
241
            }
242
        }
243
      }
244
    }
245

246
    renderedString += `>`;
92✔
247
    this.push(renderedString);
92✔
248

249
    if (String(type).match(/[\s\n/='"\0<>]/)) {
92!
250
      throw renderedString;
×
251
    }
252

253
    if (isVoidElement) {
92✔
254
      return;
8✔
255
    } else {
256
      if (html) {
84✔
257
        this.push(html);
1✔
258
        this.push(`</${type}>`);
1✔
259
        return;
1✔
260
      }
261
    }
262
    const childFlags = vNode.childFlags;
83✔
263

264
    if (childFlags === ChildFlags.HasInvalidChildren) {
83✔
265
      this.push(`</${type}>`);
28✔
266
      return;
28✔
267
    }
268

269
    return Promise.resolve(
55✔
270
      this.renderChildren(vNode.children, context, childFlags),
271
    ).then(() => {
272
      this.push(`</${type}>`);
55✔
273
    });
274
  }
275
}
276

277
export function streamAsString(node) {
69✔
278
  return new RenderStream(node);
73✔
279
}
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