Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/bundlers/default/src/DefaultBundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -1575,7 +1575,6 @@ function createIdealGraph(
asset.env.context !== bundle.env.context
? false
: bundle.needsStableName,
bundleBehavior: bundle.bundleBehavior,
type: asset.type,
target: bundle.target,
env: asset.env,
Expand Down
38 changes: 28 additions & 10 deletions packages/core/core/src/BundleGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -1503,8 +1503,15 @@ export default class BundleGraph {

getBundlesInBundleGroup(
bundleGroup: BundleGroup,
opts?: {|includeInline: boolean|},
opts?: {|
recursive?: boolean,
includeInline?: boolean,
includeIsolated?: boolean,
|},
): Array<Bundle> {
let recursive = opts?.recursive ?? true;
let includeInline = opts?.includeInline ?? false;
let includeIsolated = opts?.includeIsolated ?? true;
let bundles: Set<Bundle> = new Set();
for (let bundleNodeId of this._graph.getNodeIdsConnectedFrom(
this._graph.getNodeIdByContentKey(getBundleGroupId(bundleGroup)),
Expand All @@ -1514,16 +1521,17 @@ export default class BundleGraph {
invariant(bundleNode.type === 'bundle');
let bundle = bundleNode.value;
if (
opts?.includeInline ||
bundle.bundleBehavior !== BundleBehavior.inline
bundle.bundleBehavior == null ||
(includeInline && bundle.bundleBehavior === BundleBehavior.inline) ||
(includeIsolated && bundle.bundleBehavior === BundleBehavior.isolated)
) {
bundles.add(bundle);
}

for (let referencedBundle of this.getReferencedBundles(bundle, {
includeInline: opts?.includeInline,
})) {
bundles.add(referencedBundle);
if (recursive) {
for (let referencedBundle of this.getReferencedBundles(bundle, opts)) {
bundles.add(referencedBundle);
}
}
}

Expand All @@ -1532,10 +1540,15 @@ export default class BundleGraph {

getReferencedBundles(
bundle: Bundle,
opts?: {|recursive?: boolean, includeInline?: boolean|},
opts?: {|
recursive?: boolean,
includeInline?: boolean,
includeIsolated?: boolean,
|},
): Array<Bundle> {
let recursive = opts?.recursive ?? true;
let includeInline = opts?.includeInline ?? false;
let includeIsolated = opts?.includeIsolated ?? true;
let referencedBundles = new Set();
this._graph.dfs({
visit: (nodeId, _, actions) => {
Expand All @@ -1549,10 +1562,15 @@ export default class BundleGraph {
}

if (
includeInline ||
node.value.bundleBehavior !== BundleBehavior.inline
node.value.bundleBehavior == null ||
(includeInline &&
node.value.bundleBehavior === BundleBehavior.inline) ||
(includeIsolated &&
node.value.bundleBehavior === BundleBehavior.isolated)
) {
referencedBundles.add(node.value);
} else if (node.value.bundleBehavior === BundleBehavior.isolated) {
actions.skipChildren();
}

if (!recursive) {
Expand Down
12 changes: 10 additions & 2 deletions packages/core/core/src/public/BundleGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,11 @@ export default class BundleGraph<TBundle: IBundle>

getReferencedBundles(
bundle: IBundle,
opts?: {|recursive?: boolean, includeInline?: boolean|},
opts?: {|
recursive?: boolean,
includeInline?: boolean,
includeIsolated?: boolean,
|},
): Array<TBundle> {
return this.#graph
.getReferencedBundles(bundleToInternalBundle(bundle), opts)
Expand Down Expand Up @@ -193,7 +197,11 @@ export default class BundleGraph<TBundle: IBundle>

getBundlesInBundleGroup(
bundleGroup: IBundleGroup,
opts?: {|includeInline: boolean|},
opts?: {|
recursive?: boolean,
includeInline?: boolean,
includeIsolated?: boolean,
|},
): Array<TBundle> {
return this.#graph
.getBundlesInBundleGroup(
Expand Down
103 changes: 103 additions & 0 deletions packages/core/integration-tests/test/react-ssg.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import assert from 'assert';
import path from 'path';
import {bundle, overlayFS, fsFixture, assertBundles} from '@parcel/test-utils';
import nullthrows from 'nullthrows';

describe('react static', function () {
let count = 0;
Expand Down Expand Up @@ -466,4 +467,106 @@ describe('react static', function () {
let output = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
assert(output.includes('<link rel="stylesheet"'));
});

it('should support nested server entries', async function () {
await fsFixture(overlayFS, dir)`
index.jsx:
import {A} from './a';
import {B} from './b';
import './bootstrap';
export default async function Index() {
return (
<html>
<body>
<Switch>
<A />
<B />
</Switch>
</body>
</html>
);
}

function Switch({children}) {
return children[0];
}

a.jsx:
"use server-entry";
import {Client1} from './client1';
export function A() {
return <Client1 />;
}

b.jsx:
"use server-entry";
import {Client2} from './client2';
export function B() {
return <Client2 />;
}

client1.jsx:
"use client";
import './client1.css';
export function Client1() {
return <span>Client 1</span>;
}

client1.css:
body { color: red }

client2.jsx:
"use client";
import './client2.css';
export function Client2() {
return <span>Client 2</span>;
}

client2.css:
body { color: green }

bootstrap.js:
"use client-entry";
`;

let b = await bundle(path.join(dir, '/index.jsx'), {
inputFS: overlayFS,
});

let output = await overlayFS.readFile(b.getBundles()[0].filePath, 'utf8');
let bundles = b.getBundles();
let assets = {};
b.traverse(node => {
if (node.type === 'asset') {
assets[path.basename(node.value.filePath)] = node.value;
}
});
let client1Bundle = nullthrows(
bundles.find(b => b.hasAsset(assets['client1.jsx'])),
);
let client1CSS = nullthrows(
bundles.find(b => b.hasAsset(assets['client1.css'])),
);
let client2Bundle = nullthrows(
bundles.find(b => b.hasAsset(assets['client2.jsx'])),
);
let client2CSS = nullthrows(
bundles.find(b => b.hasAsset(assets['client2.css'])),
);
let bootstrapBundle = nullthrows(
bundles.find(b => b.hasAsset(assets['bootstrap.js'])),
);
let scripts = Array.from(output.matchAll(/<script.*?src="(.*?)"/g)).map(
b => b[1],
);
assert(scripts.includes('/' + path.basename(client1Bundle.filePath)));
assert(scripts.includes('/' + path.basename(bootstrapBundle.filePath)));
assert(!scripts.includes('/' + path.basename(client2Bundle.filePath)));

let links = Array.from(output.matchAll(/<link.*?href="(.*?)"/g)).map(
b => b[1],
);
assert(links.includes('/' + path.basename(client1CSS.filePath)));
assert(!links.includes('/' + path.basename(client2CSS.filePath)));
});
});
12 changes: 10 additions & 2 deletions packages/core/types-internal/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,11 @@ export interface BundleGraph<TBundle: Bundle> {
/** Returns a list of bundles that load together in the given bundle group. */
getBundlesInBundleGroup(
bundleGroup: BundleGroup,
opts?: {|includeInline: boolean|},
opts?: {|
recursive?: boolean,
includeInline?: boolean,
includeIsolated?: boolean,
|},
): Array<TBundle>;
/** Returns a list of bundles that this bundle loads asynchronously. */
getChildBundles(bundle: Bundle): Array<TBundle>;
Expand All @@ -1558,7 +1562,11 @@ export interface BundleGraph<TBundle: Bundle> {
/** Returns a list of bundles that are referenced by this bundle. By default, inline bundles are excluded. */
getReferencedBundles(
bundle: Bundle,
opts?: {|recursive?: boolean, includeInline?: boolean|},
opts?: {|
recursive?: boolean,
includeInline?: boolean,
includeIsolated?: boolean,
|},
): Array<TBundle>;
/** Returns a list of bundles that reference this bundle. */
getReferencingBundles(bundle: Bundle): Array<TBundle>;
Expand Down
31 changes: 22 additions & 9 deletions packages/packagers/react-static/src/ReactStaticPackager.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export default (new Packager({
let entry;
for (let b of bundleGraph.getReferencedBundles(bundle, {
includeInline: false,
includeIsolated: false,
})) {
if (b.type === 'css') {
resources.push(
Expand Down Expand Up @@ -238,7 +239,7 @@ async function loadBundleUncached(
) => Async<{|contents: Blob|}>,
) {
// Load all asset contents.
let queue = new PromiseQueue<Array<[string, [Asset, string]]>>({
let queue = new PromiseQueue<Array<[string, [NamedBundle, Asset, string]]>>({
maxConcurrent: 32,
});
bundle.traverse(node => {
Expand All @@ -262,7 +263,7 @@ async function loadBundleUncached(
return [
[
entryBundle.id,
[nullthrows(entryBundle.getMainEntry()), contents],
[entryBundle, nullthrows(entryBundle.getMainEntry()), contents],
],
];
});
Expand All @@ -278,23 +279,36 @@ async function loadBundleUncached(
}
} else if (node.type === 'asset') {
let asset = node.value;
queue.add(async () => [[asset.id, [asset, await asset.getCode()]]]);
queue.add(async () => [
[asset.id, [bundle, asset, await asset.getCode()]],
]);
}
});

let assets = new Map<string, [Asset, string]>(
for (let b of bundleGraph.getReferencedBundles(bundle)) {
queue.add(async () => {
let {assets: subAssets} = await loadBundle(
b,
bundleGraph,
getInlineBundleContents,
);
return Array.from(subAssets);
});
}

let assets = new Map<string, [NamedBundle, Asset, string]>(
(await queue.run()).flatMap(v => v),
);
let assetsByFilePath = new Map<string, string>();
let assetsByPublicId = new Map<string, string>();
for (let [asset] of assets.values()) {
for (let [, asset] of assets.values()) {
assetsByFilePath.set(getCacheKey(asset), asset.id);
assetsByPublicId.set(bundleGraph.getAssetPublicId(asset), asset.id);
}

// Load an asset into the module system by id.
let loadAsset = (id: string) => {
let [asset, code] = nullthrows(assets.get(id));
let [bundle, asset, code] = nullthrows(assets.get(id));
let cacheKey = getCacheKey(asset);
let cachedModule = moduleCache.get(cacheKey);
if (cachedModule) {
Expand Down Expand Up @@ -376,9 +390,9 @@ async function loadBundleUncached(
bundleGraph,
getInlineBundleContents,
);
for (let [id, [asset, code]] of subAssets) {
for (let [id, [bundle, asset, code]] of subAssets) {
if (!assets.has(id)) {
assets.set(id, [asset, code]);
assets.set(id, [bundle, asset, code]);
assetsByFilePath.set(getCacheKey(asset), asset.id);
assetsByPublicId.set(bundleGraph.getAssetPublicId(asset), asset.id);
}
Expand Down Expand Up @@ -456,7 +470,6 @@ function runModule(
require: (id: string) => any,
parcelRequire: (id: string) => any,
) {
// code = code.replace(/import\((['"].*?['"])\)/g, (_, m) => `parcelRequire.load(${m[0] + m.slice(3)})`);
let moduleFunction = vm.compileFunction(
code,
[
Expand Down
9 changes: 7 additions & 2 deletions packages/runtimes/rsc/src/RSCRuntime.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ export default (new Runtime({
let bundles;
let async = bundleGraph.resolveAsyncDependency(node.value, bundle);
if (async?.type === 'bundle_group') {
bundles = bundleGraph.getBundlesInBundleGroup(async.value);
bundles = bundleGraph.getBundlesInBundleGroup(async.value, {
includeIsolated: false,
});
} else {
bundles = bundleGraph.getReferencedBundles(bundle);
bundles = bundleGraph.getReferencedBundles(bundle, {
includeIsolated: false,
});
}

let importMap = {};
Expand Down Expand Up @@ -208,6 +212,7 @@ export default (new Runtime({
if (asyncResolution?.type === 'bundle_group') {
let bundles = bundleGraph.getBundlesInBundleGroup(
asyncResolution.value,
{includeIsolated: false},
);
let resources = [];
let js = [];
Expand Down