Skip to content

Commit 03b265f

Browse files
committed
Automatically inject CSS resources into RSC stream
1 parent 337c31c commit 03b265f

File tree

28 files changed

+1070
-193
lines changed

28 files changed

+1070
-193
lines changed

.prettierignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ packages/*/*/test/dist
66
packages/*/*/test/mochareporters.json
77
packages/*/*/test/fixtures
88
packages/*/*/test/.parcel-cache
9+
packages/examples/react-server-components/src/server.js
910
vendor
1011
tmp
1112
.parcel-cache

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

+91-78
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,8 @@ describe('react server components', function () {
3131
targets: {
3232
default: {
3333
context: 'react-server',
34-
outputFormat: 'commonjs',
3534
},
3635
},
37-
'@parcel/resolver-default': {
38-
packageExports: true,
39-
},
40-
'@parcel/bundler-default': {
41-
minBundleSize: 0,
42-
},
4336
}),
4437
);
4538

@@ -383,7 +376,6 @@ describe('react server components', function () {
383376
'client.jsx',
384377
'react-jsx-dev-runtime.development.js',
385378
'index.js',
386-
'jsx-dev-runtime.js',
387379
'react.development.js',
388380
],
389381
},
@@ -487,7 +479,6 @@ describe('react server components', function () {
487479
assets: [
488480
'client.jsx',
489481
'index.js',
490-
'jsx-dev-runtime.js',
491482
'react-jsx-dev-runtime.development.js',
492483
'react.development.js',
493484
],
@@ -610,7 +601,6 @@ describe('react server components', function () {
610601
// Client: shared bundle.
611602
assets: [
612603
'index.js',
613-
'jsx-dev-runtime.js',
614604
'react-jsx-dev-runtime.development.js',
615605
'react.development.js',
616606
],
@@ -620,32 +610,27 @@ describe('react server components', function () {
620610
);
621611
});
622612

623-
it('should support <Resources>', async function () {
613+
it('should support inject CSS resources', async function () {
624614
await fsFixture(overlayFS, dir)`
615+
index.jsx:
616+
import {Server} from './Page.jsx';
617+
function render() {
618+
return <Server />;
619+
}
620+
output = {render};
621+
625622
Page.jsx:
626-
import {Client} from './Client';
627-
import {Resources} from '@parcel/runtime-rsc';
623+
"use server-entry";
628624
import './server.css';
629625
export function Server() {
630-
return <><Resources /><Client /></>;
626+
return <h1>Server</h1>;
631627
}
632-
output = {Resources};
633628
634629
server.css:
635630
.server { color: red }
636-
637-
Client.jsx:
638-
"use client";
639-
import './client.css';
640-
export function Client() {
641-
return <p>Client</p>;
642-
}
643-
644-
client.css:
645-
.client { color: green }
646631
`;
647632

648-
let b = await bundle(path.join(dir, '/Page.jsx'), {
633+
let b = await bundle(path.join(dir, '/index.jsx'), {
649634
inputFS: overlayFS,
650635
targets: ['default'],
651636
defaultTargetOptions: {
@@ -657,22 +642,22 @@ describe('react server components', function () {
657642
b,
658643
[
659644
{
660-
assets: ['Page.jsx', 'resources.js'],
645+
assets: ['index.jsx'],
661646
},
662647
{
663-
assets: ['Client.jsx'],
648+
assets: ['Page.jsx'],
664649
},
665650
{
666-
assets: ['server.css', 'client.css'],
651+
assets: ['server.css'],
667652
},
668653
],
669654
{skipNodeModules: true},
670655
);
671656

672-
let res = (await run(b, null, {require: false})).output;
673-
let resources = res.Resources();
657+
let res = (await run(b, {output: null}, {require: false})).output;
658+
let output = res.render();
674659

675-
let link = resources.props.children;
660+
let link = output.props.children[0];
676661
assert.equal(link.type, 'link');
677662
assert.equal(link.props.rel, 'stylesheet');
678663
assert.equal(link.props.precedence, 'default');
@@ -687,14 +672,17 @@ describe('react server components', function () {
687672

688673
it('should generate an inline script for bootstrap with "use client-entry"', async function () {
689674
await fsFixture(overlayFS, dir)`
675+
index.jsx:
676+
import {Server} from './Page';
677+
output = {Server};
678+
690679
Page.jsx:
680+
"use server-entry";
691681
import {Client} from './Client';
692-
import {Resources} from '@parcel/runtime-rsc';
693682
import './client-entry.jsx';
694683
export function Server() {
695-
return <><Resources /><Client /></>;
684+
return <Client />;
696685
}
697-
output = {Resources};
698686
699687
client-entry.jsx:
700688
"use client-entry";
@@ -707,7 +695,7 @@ describe('react server components', function () {
707695
}
708696
`;
709697

710-
let b = await bundle(path.join(dir, '/Page.jsx'), {
698+
let b = await bundle(path.join(dir, '/index.jsx'), {
711699
inputFS: overlayFS,
712700
targets: ['default'],
713701
defaultTargetOptions: {
@@ -719,7 +707,10 @@ describe('react server components', function () {
719707
b,
720708
[
721709
{
722-
assets: ['Page.jsx', 'resources.js'],
710+
assets: ['index.jsx'],
711+
},
712+
{
713+
assets: ['Page.jsx'],
723714
},
724715
{
725716
assets: ['client-entry.jsx', 'Client.jsx'],
@@ -729,12 +720,11 @@ describe('react server components', function () {
729720
);
730721

731722
let res = await run(b, null, {require: false});
732-
let resources = res.output.Resources();
733723
let parcelRequireName = nullthrows(
734724
Object.keys(res).find(k => /^parcelRequire(.+)$/.test(k)),
735725
);
736726
let clientEntry;
737-
b.getBundles()[1].traverseAssets(a => {
727+
b.getBundles()[2].traverseAssets(a => {
738728
if (
739729
Array.isArray(a.meta.directives) &&
740730
a.meta.directives.includes('use client-entry')
@@ -743,41 +733,35 @@ describe('react server components', function () {
743733
}
744734
});
745735

746-
let script = resources.props.children;
747-
assert.equal(script.type, 'script');
748-
assert.equal(script.props.type, 'module');
749736
assert.equal(
750-
script.props.children,
751-
`import "/${path.basename(
752-
b.getBundles()[1].filePath,
753-
)}";${parcelRequireName}("${b.getAssetPublicId(
737+
res.output.Server.bootstrapScript,
738+
`Promise.all([import("/${path.basename(
739+
b.getBundles()[2].filePath,
740+
)}")]).then(()=>${parcelRequireName}("${b.getAssetPublicId(
754741
nullthrows(clientEntry),
755-
)}")`,
742+
)}"))`,
756743
);
757744
});
758745

759-
it('dynamic import works with client components', async function () {
746+
it('dynamic import of server component to client component', async function () {
760747
await fsFixture(overlayFS, dir)`
761748
Page.jsx:
762-
import {Resources} from '@parcel/runtime-rsc/resources';
763749
import './server.css';
764750
export async function Server() {
765751
let {Client} = await import('./Client');
766-
return [<Resources />, <Client />];
752+
return <Client />;
767753
}
768-
output = {Server, Resources};
754+
output = {Server};
769755
770756
server.css:
771757
.server { color: red }
772758
773759
Client.jsx:
774760
"use client";
775-
import {Resources} from '@parcel/runtime-rsc/resources';
776761
import './client.css';
777762
export function Client() {
778-
return <p><Resources />Client</p>;
763+
return <p>Client</p>;
779764
}
780-
output = {Resources};
781765
782766
client.css:
783767
.client { color: green }
@@ -795,10 +779,10 @@ describe('react server components', function () {
795779
b,
796780
[
797781
{
798-
assets: ['Page.jsx', 'resources.js'],
782+
assets: ['Page.jsx'],
799783
},
800784
{
801-
assets: ['Client.jsx', 'resources.js'],
785+
assets: ['Client.jsx'],
802786
},
803787
{
804788
assets: ['server.css'],
@@ -813,17 +797,16 @@ describe('react server components', function () {
813797
let res = (await run(b, null, {require: false})).output;
814798
let result = await res.Server();
815799
assert.equal(
816-
result[1].type.$$typeof,
800+
result.props.children[1].type.$$typeof,
817801
Symbol.for('react.client.reference'),
818802
);
819-
assert.equal(result[1].type.$$name, 'Client');
820-
assert.equal(typeof result[1].type.$$id, 'string');
821-
assert.deepEqual(result[1].type.$$bundles, [
803+
assert.equal(result.props.children[1].type.$$name, 'Client');
804+
assert.equal(typeof result.props.children[1].type.$$id, 'string');
805+
assert.deepEqual(result.props.children[1].type.$$bundles, [
822806
path.basename(b.getBundles()[1].filePath),
823807
]);
824808

825-
let resources = res.Resources();
826-
let link = resources.props.children;
809+
let link = result.props.children[0];
827810
assert.equal(link.type, 'link');
828811
assert.equal(link.props.rel, 'stylesheet');
829812
assert.equal(link.props.precedence, 'default');
@@ -834,31 +817,59 @@ describe('react server components', function () {
834817
nullthrows(
835818
b
836819
.getBundles()
837-
.find(b => b.type === 'css' && b.name.startsWith('Page')),
820+
.find(b => b.type === 'css' && b.name.startsWith('Client')),
838821
).filePath,
839822
),
840823
);
824+
});
841825

842-
let entryAsset;
843-
b.getBundles()[1].traverseAssets(a => {
844-
if (a.filePath.endsWith('Client.jsx')) {
845-
entryAsset = a;
826+
it('dynamic import of server component to server component', async function () {
827+
await fsFixture(overlayFS, dir)`
828+
Page.jsx:
829+
export async function Server() {
830+
let {Dynamic} = await import('./Dynamic');
831+
return <Dynamic />;
832+
}
833+
output = {Server};
834+
835+
Dynamic.jsx:
836+
import './dynamic.css';
837+
export function Dynamic() {
838+
return <p>Dynamic</p>;
846839
}
840+
841+
dynamic.css:
842+
.dynamic { color: green }
843+
`;
844+
845+
let b = await bundle(path.join(dir, '/Page.jsx'), {
846+
inputFS: overlayFS,
847+
targets: ['default'],
848+
defaultTargetOptions: {
849+
shouldScopeHoist,
850+
},
847851
});
848852

849-
let client = (await runBundle(
853+
assertBundles(
850854
b,
851-
b.getBundles()[1],
852-
{output: null},
853-
{require: false, entryAsset},
854-
): any);
855-
let parcelRequire = nullthrows(
856-
Object.keys(client).find(k => k.startsWith('parcelRequire')),
855+
[
856+
{
857+
assets: ['Page.jsx'],
858+
},
859+
{
860+
assets: ['Dynamic.jsx'],
861+
},
862+
{
863+
assets: ['dynamic.css'],
864+
},
865+
],
866+
{skipNodeModules: true},
857867
);
858-
client[parcelRequire](b.getAssetPublicId(nullthrows(entryAsset)));
859-
resources = client.output.Resources();
860868

861-
link = resources.props.children;
869+
let res = (await run(b, null, {require: false})).output;
870+
let result = await res.Server();
871+
872+
let link = result.props.children[0];
862873
assert.equal(link.type, 'link');
863874
assert.equal(link.props.rel, 'stylesheet');
864875
assert.equal(link.props.precedence, 'default');
@@ -869,7 +880,9 @@ describe('react server components', function () {
869880
nullthrows(
870881
b
871882
.getBundles()
872-
.find(b => b.type === 'css' && b.name.startsWith('Client')),
883+
.find(
884+
b => b.type === 'css' && b.name.startsWith('Dynamic'),
885+
),
873886
).filePath,
874887
),
875888
);

0 commit comments

Comments
 (0)