A tool for people curious about the React Server Components protocol rscexplorer.dev/
rsc react

better display multiline jsx

+343 -243
+83 -14
src/client/ui/TreeView.tsx
··· 448 448 ([k]) => !["key", "ref", "__self", "__source"].includes(k), 449 449 ); 450 450 451 + const totalProps = propEntries.length + (key != null ? 1 : 0); 452 + const multiline = totalProps >= 2; 453 + 451 454 if ( 452 455 children === undefined || 453 456 children === null || 454 457 (Array.isArray(children) && children.length === 0) 455 458 ) { 459 + if (multiline) { 460 + return ( 461 + <> 462 + <span className={tagClass}>&lt;{tagName}</span> 463 + {key != null && ( 464 + <> 465 + {"\n"} 466 + {padInner} 467 + <span className="TreeView-propName">key</span>= 468 + <span className="TreeView-string">"{key}"</span> 469 + </> 470 + )} 471 + {propEntries.map(([k, v]) => ( 472 + <React.Fragment key={k}> 473 + {"\n"} 474 + {padInner} 475 + <JSXProp name={k} value={v} indent={indent + 1} ancestors={ancestors} /> 476 + </React.Fragment> 477 + ))} 478 + {"\n"} 479 + {pad} 480 + <span className={tagClass}>/&gt;</span> 481 + </> 482 + ); 483 + } 456 484 return ( 457 485 <> 458 486 <span className={tagClass}>&lt;{tagName}</span> ··· 464 492 </> 465 493 )} 466 494 {propEntries.map(([k, v]) => ( 467 - <JSXProp key={k} name={k} value={v} indent={indent + 1} ancestors={ancestors} /> 495 + <React.Fragment key={k}> 496 + {" "} 497 + <JSXProp name={k} value={v} indent={indent} ancestors={ancestors} /> 498 + </React.Fragment> 468 499 ))} 469 500 <span className={tagClass}> /&gt;</span> 470 501 </> ··· 472 503 } 473 504 474 505 const hasComplexChildren = typeof children !== "string" && typeof children !== "number"; 506 + 507 + if (multiline) { 508 + return ( 509 + <> 510 + <span className={tagClass}>&lt;{tagName}</span> 511 + {key != null && ( 512 + <> 513 + {"\n"} 514 + {padInner} 515 + <span className="TreeView-propName">key</span>= 516 + <span className="TreeView-string">"{key}"</span> 517 + </> 518 + )} 519 + {propEntries.map(([k, v]) => ( 520 + <React.Fragment key={k}> 521 + {"\n"} 522 + {padInner} 523 + <JSXProp name={k} value={v} indent={indent + 1} ancestors={ancestors} /> 524 + </React.Fragment> 525 + ))} 526 + {"\n"} 527 + {pad} 528 + <span className={tagClass}>&gt;</span> 529 + {hasComplexChildren ? ( 530 + <> 531 + {"\n"} 532 + {padInner} 533 + <JSXChildren value={children} indent={indent + 1} ancestors={ancestors} /> 534 + {"\n"} 535 + {pad} 536 + </> 537 + ) : ( 538 + <JSXChildren value={children} indent={indent + 1} ancestors={ancestors} /> 539 + )} 540 + <span className={tagClass}>&lt;/{tagName}&gt;</span> 541 + </> 542 + ); 543 + } 544 + 475 545 return ( 476 546 <> 477 547 <span className={tagClass}>&lt;{tagName}</span> ··· 483 553 </> 484 554 )} 485 555 {propEntries.map(([k, v]) => ( 486 - <JSXProp key={k} name={k} value={v} indent={indent + 1} ancestors={ancestors} /> 556 + <React.Fragment key={k}> 557 + {" "} 558 + <JSXProp name={k} value={v} indent={indent} ancestors={ancestors} /> 559 + </React.Fragment> 487 560 ))} 488 561 <span className={tagClass}>&gt;</span> 489 562 {hasComplexChildren ? ( ··· 513 586 if (typeof value === "string") { 514 587 return ( 515 588 <> 516 - {" "} 517 589 <span className="TreeView-propName">{name}</span>= 518 590 <span className="TreeView-string">"{escapeHtml(value)}"</span> 519 591 </> 520 592 ); 521 593 } 522 594 if (isReactElement(value)) { 523 - const pad = " ".repeat(indent); 524 - const closePad = " ".repeat(indent - 1); 595 + const innerPad = " ".repeat(indent + 1); 596 + const closePad = " ".repeat(indent); 525 597 return ( 526 598 <> 527 - {" "} 528 599 <span className="TreeView-propName">{name}</span>={"{"} 529 600 {"\n"} 530 - {pad} 531 - <JSXValue value={value} indent={indent} ancestors={ancestors} /> 601 + {innerPad} 602 + <JSXValue value={value} indent={indent + 1} ancestors={ancestors} /> 532 603 {"\n"} 533 604 {closePad} 534 605 {"}"} ··· 536 607 ); 537 608 } 538 609 if (Array.isArray(value) && value.some((v) => isReactElement(v))) { 539 - const pad = " ".repeat(indent); 540 - const closePad = " ".repeat(indent - 1); 610 + const itemPad = " ".repeat(indent + 1); 611 + const closePad = " ".repeat(indent); 541 612 return ( 542 613 <> 543 - {" "} 544 614 <span className="TreeView-propName">{name}</span>={"{["} 545 615 {"\n"} 546 616 {value.map((v, i) => ( 547 617 <React.Fragment key={i}> 548 - {pad} 549 - <JSXValue value={v} indent={indent} ancestors={ancestors} /> 618 + {itemPad} 619 + <JSXValue value={v} indent={indent + 1} ancestors={ancestors} /> 550 620 {i < value.length - 1 ? "," : ""} 551 621 {"\n"} 552 622 </React.Fragment> ··· 558 628 } 559 629 return ( 560 630 <> 561 - {" "} 562 631 <span className="TreeView-propName">{name}</span>={"{"} 563 632 <JSXValue value={value} indent={indent} ancestors={ancestors} /> 564 633 {"}"}
+8 -2
tests/clientref.spec.ts
··· 34 34 "<div> 35 35 <h1>Client Reference</h1> 36 36 <div style={{ display: "flex", gap: 12 }}> 37 - <ThemedBox theme={{ background: "#1a1a1a", color: "#fff" }} label="Dark" /> 38 - <ThemedBox theme={{ background: "#f5f5f5", color: "#000" }} label="Light" /> 37 + <ThemedBox 38 + theme={{ background: "#1a1a1a", color: "#fff" }} 39 + label="Dark" 40 + /> 41 + <ThemedBox 42 + theme={{ background: "#f5f5f5", color: "#000" }} 43 + label="Light" 44 + /> 39 45 </div> 40 46 </div>" 41 47 `);
+10 -10
tests/errors.spec.ts
··· 29 29 <h1>Error Handling</h1> 30 30 <ErrorBoundary fallback={ 31 31 <div style={{ 32 - padding: 16, 33 - background: "#fee", 34 - borderRadius: 8, 35 - color: "#c00" 36 - }}> 32 + padding: 16, 33 + background: "#fee", 34 + borderRadius: 8, 35 + color: "#c00" 36 + }}> 37 37 <strong>Failed to load user</strong> 38 38 <p style={{ margin: "4px 0 0" }}>Please try again later.</p> 39 39 </div> ··· 58 58 <h1>Error Handling</h1> 59 59 <ErrorBoundary fallback={ 60 60 <div style={{ 61 - padding: 16, 62 - background: "#fee", 63 - borderRadius: 8, 64 - color: "#c00" 65 - }}> 61 + padding: 16, 62 + background: "#fee", 63 + borderRadius: 8, 64 + color: "#c00" 65 + }}> 66 66 <strong>Failed to load user</strong> 67 67 <p style={{ margin: "4px 0 0" }}>Please try again later.</p> 68 68 </div>
+168 -168
tests/kitchensink.spec.ts
··· 52 52 <p>Loading...</p> 53 53 }> 54 54 <DataDisplay data={{ 55 - primitives: { 56 - null: null, 57 - true: true, 58 - false: false, 59 - int: 42, 60 - float: 3.14159, 61 - string: "hello world", 62 - empty: "", 63 - dollar: "$special", 64 - unicode: "Hello 世界 🌍" 65 - }, 66 - special: { 67 - negZero: -0, 68 - inf: Infinity, 69 - negInf: -Infinity, 70 - nan: NaN 71 - }, 72 - types: { 73 - date: Date(2024-01-15T12:00:00.000Z), 74 - bigint: 12345678901234567890n, 75 - symbol: Symbol(mySymbol) 76 - }, 77 - binary: { 78 - uint8: Uint8Array(5) [1, 2, 3, 4, 5], 79 - int32: Int32Array(3) [-1, 0, 2147483647], 80 - float64: Float64Array(2) [3.14159, 2.71828] 81 - }, 82 - collections: { 83 - map: Map(2) { 84 - "a" => 1, 85 - "b" => { nested: true } 86 - }, 87 - set: Set(3) { 88 - 1, 89 - 2, 90 - "three" 91 - }, 92 - formData: FormData { 93 - key: "value" 94 - }, 95 - blob: Blob(5 bytes, "text/plain") 96 - }, 97 - arrays: { 98 - simple: [1, 2, 3], 99 - sparse: [ 100 - 1, 101 - empty, 102 - empty, 103 - 4 104 - ], 105 - nested: [[1], [2, [3]]] 106 - }, 107 - objects: { 108 - simple: { a: 1 }, 109 - nested: { 110 - x: { 111 - y: { z: "deep" } 112 - } 113 - } 114 - }, 115 - elements: { 116 - div: <div className="test">Hello</div>, 117 - fragment: [ 118 - <span>a</span>, 119 - <span>b</span> 120 - ], 121 - suspense: <Suspense fallback="..."> 122 - <p>content</p> 123 - </Suspense> 55 + primitives: { 56 + null: null, 57 + true: true, 58 + false: false, 59 + int: 42, 60 + float: 3.14159, 61 + string: "hello world", 62 + empty: "", 63 + dollar: "$special", 64 + unicode: "Hello 世界 🌍" 65 + }, 66 + special: { 67 + negZero: -0, 68 + inf: Infinity, 69 + negInf: -Infinity, 70 + nan: NaN 71 + }, 72 + types: { 73 + date: Date(2024-01-15T12:00:00.000Z), 74 + bigint: 12345678901234567890n, 75 + symbol: Symbol(mySymbol) 76 + }, 77 + binary: { 78 + uint8: Uint8Array(5) [1, 2, 3, 4, 5], 79 + int32: Int32Array(3) [-1, 0, 2147483647], 80 + float64: Float64Array(2) [3.14159, 2.71828] 81 + }, 82 + collections: { 83 + map: Map(2) { 84 + "a" => 1, 85 + "b" => { nested: true } 124 86 }, 125 - promises: { 126 - resolved: "immediate", 127 - delayed: Pending 87 + set: Set(3) { 88 + 1, 89 + 2, 90 + "three" 128 91 }, 129 - iterators: { 130 - sync: Iterator {} 92 + formData: FormData { 93 + key: "value" 131 94 }, 132 - refs: { 133 - dup: { 134 - a: { id: 1 }, 135 - b: { id: 1 } 136 - }, 137 - cyclic: { 138 - name: "cyclic", 139 - self: [Circular] 95 + blob: Blob(5 bytes, "text/plain") 96 + }, 97 + arrays: { 98 + simple: [1, 2, 3], 99 + sparse: [ 100 + 1, 101 + empty, 102 + empty, 103 + 4 104 + ], 105 + nested: [[1], [2, [3]]] 106 + }, 107 + objects: { 108 + simple: { a: 1 }, 109 + nested: { 110 + x: { 111 + y: { z: "deep" } 140 112 } 113 + } 114 + }, 115 + elements: { 116 + div: <div className="test">Hello</div>, 117 + fragment: [ 118 + <span>a</span>, 119 + <span>b</span> 120 + ], 121 + suspense: <Suspense fallback="..."> 122 + <p>content</p> 123 + </Suspense> 124 + }, 125 + promises: { 126 + resolved: "immediate", 127 + delayed: Pending 128 + }, 129 + iterators: { 130 + sync: Iterator {} 131 + }, 132 + refs: { 133 + dup: { 134 + a: { id: 1 }, 135 + b: { id: 1 } 141 136 }, 142 - action: [Function: serverAction] 143 - }} /> 137 + cyclic: { 138 + name: "cyclic", 139 + self: [Circular] 140 + } 141 + }, 142 + action: [Function: serverAction] 143 + }} /> 144 144 </Suspense> 145 145 </div>" 146 146 `); ··· 158 158 <p>Loading...</p> 159 159 }> 160 160 <DataDisplay data={{ 161 - primitives: { 162 - null: null, 163 - true: true, 164 - false: false, 165 - int: 42, 166 - float: 3.14159, 167 - string: "hello world", 168 - empty: "", 169 - dollar: "$special", 170 - unicode: "Hello 世界 🌍" 171 - }, 172 - special: { 173 - negZero: -0, 174 - inf: Infinity, 175 - negInf: -Infinity, 176 - nan: NaN 177 - }, 178 - types: { 179 - date: Date(2024-01-15T12:00:00.000Z), 180 - bigint: 12345678901234567890n, 181 - symbol: Symbol(mySymbol) 182 - }, 183 - binary: { 184 - uint8: Uint8Array(5) [1, 2, 3, 4, 5], 185 - int32: Int32Array(3) [-1, 0, 2147483647], 186 - float64: Float64Array(2) [3.14159, 2.71828] 187 - }, 188 - collections: { 189 - map: Map(2) { 190 - "a" => 1, 191 - "b" => { nested: true } 192 - }, 193 - set: Set(3) { 194 - 1, 195 - 2, 196 - "three" 197 - }, 198 - formData: FormData { 199 - key: "value" 200 - }, 201 - blob: Blob(5 bytes, "text/plain") 202 - }, 203 - arrays: { 204 - simple: [1, 2, 3], 205 - sparse: [ 206 - 1, 207 - empty, 208 - empty, 209 - 4 210 - ], 211 - nested: [[1], [2, [3]]] 212 - }, 213 - objects: { 214 - simple: { a: 1 }, 215 - nested: { 216 - x: { 217 - y: { z: "deep" } 218 - } 219 - } 220 - }, 221 - elements: { 222 - div: <div className="test">Hello</div>, 223 - fragment: [ 224 - <span>a</span>, 225 - <span>b</span> 226 - ], 227 - suspense: <Suspense fallback="..."> 228 - <p>content</p> 229 - </Suspense> 161 + primitives: { 162 + null: null, 163 + true: true, 164 + false: false, 165 + int: 42, 166 + float: 3.14159, 167 + string: "hello world", 168 + empty: "", 169 + dollar: "$special", 170 + unicode: "Hello 世界 🌍" 171 + }, 172 + special: { 173 + negZero: -0, 174 + inf: Infinity, 175 + negInf: -Infinity, 176 + nan: NaN 177 + }, 178 + types: { 179 + date: Date(2024-01-15T12:00:00.000Z), 180 + bigint: 12345678901234567890n, 181 + symbol: Symbol(mySymbol) 182 + }, 183 + binary: { 184 + uint8: Uint8Array(5) [1, 2, 3, 4, 5], 185 + int32: Int32Array(3) [-1, 0, 2147483647], 186 + float64: Float64Array(2) [3.14159, 2.71828] 187 + }, 188 + collections: { 189 + map: Map(2) { 190 + "a" => 1, 191 + "b" => { nested: true } 230 192 }, 231 - promises: { 232 - resolved: "immediate", 233 - delayed: "delayed" 193 + set: Set(3) { 194 + 1, 195 + 2, 196 + "three" 234 197 }, 235 - iterators: { 236 - sync: Iterator {} 198 + formData: FormData { 199 + key: "value" 237 200 }, 238 - refs: { 239 - dup: { 240 - a: { id: 1 }, 241 - b: { id: 1 } 242 - }, 243 - cyclic: { 244 - name: "cyclic", 245 - self: [Circular] 201 + blob: Blob(5 bytes, "text/plain") 202 + }, 203 + arrays: { 204 + simple: [1, 2, 3], 205 + sparse: [ 206 + 1, 207 + empty, 208 + empty, 209 + 4 210 + ], 211 + nested: [[1], [2, [3]]] 212 + }, 213 + objects: { 214 + simple: { a: 1 }, 215 + nested: { 216 + x: { 217 + y: { z: "deep" } 246 218 } 219 + } 220 + }, 221 + elements: { 222 + div: <div className="test">Hello</div>, 223 + fragment: [ 224 + <span>a</span>, 225 + <span>b</span> 226 + ], 227 + suspense: <Suspense fallback="..."> 228 + <p>content</p> 229 + </Suspense> 230 + }, 231 + promises: { 232 + resolved: "immediate", 233 + delayed: "delayed" 234 + }, 235 + iterators: { 236 + sync: Iterator {} 237 + }, 238 + refs: { 239 + dup: { 240 + a: { id: 1 }, 241 + b: { id: 1 } 247 242 }, 248 - action: [Function: serverAction] 249 - }} /> 243 + cyclic: { 244 + name: "cyclic", 245 + self: [Circular] 246 + } 247 + }, 248 + action: [Function: serverAction] 249 + }} /> 250 250 </Suspense> 251 251 </div>" 252 252 `);
+70 -48
tests/pagination.spec.ts
··· 49 49 <Suspense fallback={ 50 50 <p style={{ color: "#888" }}>Loading recipes...</p> 51 51 }> 52 - <Paginator initialItems={[ 53 - <div key="1" style={{ 54 - padding: 12, 55 - marginBottom: 8, 56 - background: "#f5f5f5", 57 - borderRadius: 6 58 - }}> 59 - <strong>Pasta Carbonara</strong> 60 - <p style={{ 52 + <Paginator 53 + initialItems={[ 54 + <div 55 + key="1" 56 + style={{ 57 + padding: 12, 58 + marginBottom: 8, 59 + background: "#f5f5f5", 60 + borderRadius: 6 61 + }} 62 + > 63 + <strong>Pasta Carbonara</strong> 64 + <p style={{ 61 65 margin: "4px 0 0", 62 66 color: "#666", 63 67 fontSize: 13 64 68 }}> 65 - 25 min · Medium 66 - </p> 67 - </div>, 68 - <div key="2" style={{ 69 - padding: 12, 70 - marginBottom: 8, 71 - background: "#f5f5f5", 72 - borderRadius: 6 73 - }}> 74 - <strong>Grilled Cheese</strong> 75 - <p style={{ 69 + 25 min · Medium 70 + </p> 71 + </div>, 72 + <div 73 + key="2" 74 + style={{ 75 + padding: 12, 76 + marginBottom: 8, 77 + background: "#f5f5f5", 78 + borderRadius: 6 79 + }} 80 + > 81 + <strong>Grilled Cheese</strong> 82 + <p style={{ 76 83 margin: "4px 0 0", 77 84 color: "#666", 78 85 fontSize: 13 79 86 }}> 80 - 10 min · Easy 81 - </p> 82 - </div> 83 - ]} initialCursor={2} loadMoreAction={[Function: loadMore]} /> 87 + 10 min · Easy 88 + </p> 89 + </div> 90 + ]} 91 + initialCursor={2} 92 + loadMoreAction={[Function: loadMore]} 93 + /> 84 94 </Suspense> 85 95 </div>" 86 96 `); ··· 120 130 expect(await h.stepAll()).toMatchInlineSnapshot(` 121 131 "{ 122 132 newItems: [ 123 - <div key="3" style={{ 133 + <div 134 + key="3" 135 + style={{ 124 136 padding: 12, 125 137 marginBottom: 8, 126 138 background: "#f5f5f5", 127 139 borderRadius: 6 128 - }}> 140 + }} 141 + > 129 142 <strong>Chicken Stir Fry</strong> 130 143 <p style={{ 131 - margin: "4px 0 0", 132 - color: "#666", 133 - fontSize: 13 134 - }}> 144 + margin: "4px 0 0", 145 + color: "#666", 146 + fontSize: 13 147 + }}> 135 148 20 min · Easy 136 149 </p> 137 150 </div>, 138 - <div key="4" style={{ 151 + <div 152 + key="4" 153 + style={{ 139 154 padding: 12, 140 155 marginBottom: 8, 141 156 background: "#f5f5f5", 142 157 borderRadius: 6 143 - }}> 158 + }} 159 + > 144 160 <strong>Beef Tacos</strong> 145 161 <p style={{ 146 - margin: "4px 0 0", 147 - color: "#666", 148 - fontSize: 13 149 - }}> 162 + margin: "4px 0 0", 163 + color: "#666", 164 + fontSize: 13 165 + }}> 150 166 30 min · Medium 151 167 </p> 152 168 </div> ··· 207 223 expect(await h.stepAll()).toMatchInlineSnapshot(` 208 224 "{ 209 225 newItems: [ 210 - <div key="5" style={{ 226 + <div 227 + key="5" 228 + style={{ 211 229 padding: 12, 212 230 marginBottom: 8, 213 231 background: "#f5f5f5", 214 232 borderRadius: 6 215 - }}> 233 + }} 234 + > 216 235 <strong>Caesar Salad</strong> 217 236 <p style={{ 218 - margin: "4px 0 0", 219 - color: "#666", 220 - fontSize: 13 221 - }}> 237 + margin: "4px 0 0", 238 + color: "#666", 239 + fontSize: 13 240 + }}> 222 241 15 min · Easy 223 242 </p> 224 243 </div>, 225 - <div key="6" style={{ 244 + <div 245 + key="6" 246 + style={{ 226 247 padding: 12, 227 248 marginBottom: 8, 228 249 background: "#f5f5f5", 229 250 borderRadius: 6 230 - }}> 251 + }} 252 + > 231 253 <strong>Mushroom Risotto</strong> 232 254 <p style={{ 233 - margin: "4px 0 0", 234 - color: "#666", 235 - fontSize: 13 236 - }}> 255 + margin: "4px 0 0", 256 + color: "#666", 257 + fontSize: 13 258 + }}> 237 259 45 min · Hard 238 260 </p> 239 261 </div>
+4 -1
tests/refresh.spec.ts
··· 31 31 <Suspense fallback={ 32 32 <p>Loading...</p> 33 33 }> 34 - <Router initial={Pending} refreshAction={[Function: renderPage]} /> 34 + <Router 35 + initial={Pending} 36 + refreshAction={[Function: renderPage]} 37 + /> 35 38 </Suspense> 36 39 </div>" 37 40 `);