Skip to content

Commit 009dbd8

Browse files
committed
avoid custom jsx runtime
1 parent 8fc0c40 commit 009dbd8

File tree

12 files changed

+327
-423
lines changed

12 files changed

+327
-423
lines changed

packages/core/integration-tests/test/react-server.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,9 @@ describe('react server components', function () {
657657
let res = (await run(b, {output: null}, {require: false})).output;
658658
let output = res.render();
659659

660-
let link = output.props.children[0];
660+
output.type.$$typeof;
661+
let rendered = output.type();
662+
let link = rendered.props.children[0];
661663
assert.equal(link.type, 'link');
662664
assert.equal(link.props.rel, 'stylesheet');
663665
assert.equal(link.props.precedence, 'default');
@@ -795,7 +797,9 @@ describe('react server components', function () {
795797
);
796798

797799
let res = (await run(b, null, {require: false})).output;
798-
let result = await res.Server();
800+
let output = await res.Server();
801+
output.type.$$typeof;
802+
let result = output.type();
799803
assert.equal(
800804
result.props.children[1].type.$$typeof,
801805
Symbol.for('react.client.reference'),
@@ -867,7 +871,9 @@ describe('react server components', function () {
867871
);
868872

869873
let res = (await run(b, null, {require: false})).output;
870-
let result = await res.Server();
874+
let output = await res.Server();
875+
output.type.$$typeof;
876+
let result = output.type();
871877

872878
let link = result.props.children[0];
873879
assert.equal(link.type, 'link');

packages/core/integration-tests/test/react-ssg.js

+68
Original file line numberDiff line numberDiff line change
@@ -415,4 +415,72 @@ describe('react static', function () {
415415
assert(output.includes('<link rel="stylesheet"'));
416416
assert(output.includes('<script type="module"'));
417417
});
418+
419+
it('should support dynamic importing an object with components attached', async function () {
420+
await fsFixture(overlayFS, dir)`
421+
index.jsx:
422+
export default async function Index() {
423+
let {default: components} = await import('./server');
424+
return (
425+
<html>
426+
<body>
427+
<components.Server />
428+
</body>
429+
</html>
430+
);
431+
}
432+
433+
server.jsx:
434+
import './server.css';
435+
function Server() {
436+
return <h1>Server</h1>;
437+
}
438+
export default {Server};
439+
440+
server.css:
441+
h1 { color: red }
442+
`;
443+
444+
let b = await bundle(path.join(dir, '/index.jsx'), {
445+
inputFS: overlayFS,
446+
targets: ['default'],
447+
});
448+
449+
let output = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
450+
assert(output.includes('<link rel="stylesheet"'));
451+
});
452+
453+
it('should support dynamic importing a React.memo component', async function () {
454+
await fsFixture(overlayFS, dir)`
455+
index.jsx:
456+
export default async function Index() {
457+
let {default: Server} = await import('./server');
458+
return (
459+
<html>
460+
<body>
461+
<Server />
462+
</body>
463+
</html>
464+
);
465+
}
466+
467+
server.jsx:
468+
import './server.css';
469+
import {memo} from 'react';
470+
export default memo(function Server() {
471+
return <h1>Server</h1>;
472+
});
473+
474+
server.css:
475+
h1 { color: red }
476+
`;
477+
478+
let b = await bundle(path.join(dir, '/index.jsx'), {
479+
inputFS: overlayFS,
480+
targets: ['default'],
481+
});
482+
483+
let output = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
484+
assert(output.includes('<link rel="stylesheet"'));
485+
});
418486
});

packages/runtimes/rsc/jsx-dev-runtime.js

-25
This file was deleted.

packages/runtimes/rsc/jsx-runtime.js

-26
This file was deleted.

packages/runtimes/rsc/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"source": "./src/RSCRuntime.js",
2020
"default": "./lib/RSCRuntime.js"
2121
},
22-
"./rsc-helpers": "./rsc-helpers.js",
22+
"./rsc-helpers": "./rsc-helpers.jsx",
2323
"./jsx-dev-runtime": "./jsx-dev-runtime.js",
2424
"./jsx-runtime": "./jsx-runtime"
2525
},

packages/runtimes/rsc/rsc-helpers.js

-42
This file was deleted.

packages/runtimes/rsc/rsc-helpers.jsx

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/* eslint-env browser */
2+
/* eslint-disable react/react-in-jsx-scope */
3+
/* @jsxRuntime automatic */
4+
5+
export function createResourcesProxy(module, resources, bootstrapScript) {
6+
return new Proxy(module, {
7+
get(target, prop, receiver) {
8+
let value = Reflect.get(target, prop, receiver);
9+
return createResourcesValueProxy(value, resources, bootstrapScript);
10+
},
11+
});
12+
}
13+
14+
let cache = new WeakMap();
15+
function createResourcesValueProxy(value, resources, bootstrapScript) {
16+
if (typeof value === 'function') {
17+
let cached = cache.get(value);
18+
if (cached) {
19+
return cached;
20+
}
21+
22+
// Detect reads of `Component.prototype.isReactComponent` or `Component.$$typeof`
23+
// prior to the function being called as an indication that this is a React component.
24+
// If so, inject the resources as a sibling of the return value.
25+
let isReactComponent = false;
26+
let prototypeProxy;
27+
if (typeof value.prototype === 'object') {
28+
prototypeProxy = new Proxy(value.prototype, {
29+
get(target, prop, receiver) {
30+
if (prop === 'isReactComponent') {
31+
isReactComponent = true;
32+
}
33+
return Reflect.get(target, prop, receiver);
34+
}
35+
});
36+
}
37+
38+
let proxy = new Proxy(value, {
39+
get(target, prop, receiver) {
40+
if (prototypeProxy && prop === 'prototype') {
41+
return prototypeProxy;
42+
}
43+
44+
if (prop === '$$typeof') {
45+
isReactComponent = true;
46+
}
47+
48+
if (bootstrapScript && prop === 'bootstrapScript') {
49+
return bootstrapScript;
50+
}
51+
52+
return Reflect.get(target, prop, receiver);
53+
},
54+
apply(target, thisArg, args) {
55+
let result = Reflect.apply(target, thisArg, args);
56+
if (isReactComponent) {
57+
return <>{resources}{result}</>
58+
}
59+
return result;
60+
}
61+
});
62+
63+
cache.set(value, proxy);
64+
return proxy;
65+
} else if (value && typeof value === 'object') {
66+
let cached = cache.get(value);
67+
if (cached) {
68+
return cached;
69+
}
70+
71+
let proxy = new Proxy(value, {
72+
get(target, prop, receiver) {
73+
let value = Reflect.get(target, prop, receiver);
74+
return createResourcesValueProxy(value, resources, bootstrapScript);
75+
}
76+
});
77+
78+
cache.set(value, proxy);
79+
return proxy;
80+
}
81+
82+
return value;
83+
}
84+
85+
export function waitForCSS(url) {
86+
return new Promise((resolve, reject) => {
87+
if (typeof document === 'undefined') {
88+
return resolve();
89+
}
90+
91+
// Find a link element corresponding to this URL.
92+
let link = document.querySelector(`link[rel="stylesheet"][href="${CSS.escape(url)}"]`);
93+
if (!link) {
94+
return resolve();
95+
}
96+
97+
// If the link element already has a stylesheet associated with it, then it is already loaded.
98+
if (link.sheet) {
99+
return resolve();
100+
}
101+
102+
link.addEventListener('load', () => {
103+
resolve();
104+
});
105+
106+
link.addEventListener('error', e => {
107+
reject(e);
108+
});
109+
});
110+
}

0 commit comments

Comments
 (0)