diff --git a/fixtures/blocks/src/Router.js b/fixtures/blocks/src/Router.js
index 50f93d9d34..63e15aabfa 100644
--- a/fixtures/blocks/src/Router.js
+++ b/fixtures/blocks/src/Router.js
@@ -15,17 +15,16 @@ import React, {
} from 'react';
import {createCache, CacheProvider} from 'react/unstable-cache';
import {RouterProvider} from './client/RouterContext';
-// TODO: entry point. This can't really done in the client code.
-import loadApp from './server/App.block';
+// TODO: can't really import a server component on the client.
+import App from './server/App';
const initialUrl = window.location.pathname;
-
const initialState = {
// TODO: use this for invalidation.
cache: createCache(),
url: initialUrl,
pendingUrl: initialUrl,
- RootBlock: loadApp(initialUrl),
+ root: ,
};
function reducer(state, action) {
@@ -41,7 +40,7 @@ function reducer(state, action) {
...state,
url: action.url,
pendingUrl: action.url,
- RootBlock: action.RootBlock,
+ root: action.root,
};
default:
throw new Error();
@@ -65,7 +64,7 @@ function Router() {
// TODO: Instant Transitions, somehow.
dispatch({
type: 'completeNavigation',
- RootBlock: loadApp(url),
+ root: ,
url,
});
});
@@ -97,9 +96,7 @@ function Router() {
return (
Loading...}>
-
-
-
+ {state.root}
);
diff --git a/fixtures/blocks/src/client/ProfileNav.js b/fixtures/blocks/src/client/ProfileNav.js
new file mode 100644
index 0000000000..0eeb104afe
--- /dev/null
+++ b/fixtures/blocks/src/client/ProfileNav.js
@@ -0,0 +1,21 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+
+import {TabBar, TabLink} from '../client/TabNav';
+
+export default function ProfileNav({userId}) {
+ // TODO: Don't hardcode ID.
+ return (
+
+ Timeline
+ Bio
+
+ );
+}
diff --git a/fixtures/blocks/src/server/App.block.js b/fixtures/blocks/src/server/App.block.js
deleted file mode 100644
index a02cedd3ce..0000000000
--- a/fixtures/blocks/src/server/App.block.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-/* eslint-disable import/first */
-
-import * as React from 'react';
-import {unstable_block as block} from 'react';
-
-// Server
-
-import loadFeedPage from './FeedPage.block';
-import loadProfilePage from './ProfilePage.block';
-
-function load(url) {
- let Page;
- const segments = url.split('/').filter(Boolean);
- if (segments.length === 0) {
- Page = loadFeedPage();
- } else if (segments[0] === 'profile') {
- Page = loadProfilePage(Number(segments[1]), segments[2]);
- } else {
- throw Error('Not found');
- }
- return {Page};
-}
-
-// Client
-
-import Shell from '../client/Shell';
-
-// TODO: some notion of key.
-
-function App(props, data) {
- return (
-
-
-
- );
-}
-
-export default block(App, load);
diff --git a/fixtures/blocks/src/server/App.js b/fixtures/blocks/src/server/App.js
new file mode 100644
index 0000000000..792e1f7c76
--- /dev/null
+++ b/fixtures/blocks/src/server/App.js
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+import {matchRoute} from './ServerRouter';
+import FeedPage from './FeedPage';
+import ProfilePage from './ProfilePage';
+
+// TODO: Replace with asset reference.
+import loadShell from './Shell.block';
+
+// TODO: Router component?
+const AppRoutes = {
+ '/': props => ,
+ '/profile/:userId/*': props => (
+
+ ),
+};
+
+export default function App(props) {
+ const Shell = loadShell();
+ const match = matchRoute(props, AppRoutes);
+ return {match} ;
+}
diff --git a/fixtures/blocks/src/server/Comments.block.js b/fixtures/blocks/src/server/Comments.js
similarity index 62%
rename from fixtures/blocks/src/server/Comments.block.js
rename to fixtures/blocks/src/server/Comments.js
index 2bc4d518c8..14f37f6a99 100644
--- a/fixtures/blocks/src/server/Comments.block.js
+++ b/fixtures/blocks/src/server/Comments.js
@@ -7,28 +7,19 @@
/* eslint-disable import/first */
import * as React from 'react';
-import {unstable_block as block} from 'react';
-
-// Server
-
import {fetch} from 'react-data/fetch';
-function load(postId) {
- return {
- comments: fetch(`/comments?postId=${postId}&_expand=user`).json(),
- };
-}
+// TODO: Replace with asset reference.
+import loadLink from './Link.block';
-// Client
-
-import Link from '../client/Link';
-
-function Comments(props, data) {
+export default function Comments({postId}) {
+ const Link = loadLink();
+ const comments = fetch(`/comments?postId=${postId}&_expand=user`).json();
return (
<>
Comments
- {data.comments.slice(0, 5).map(comment => (
+ {comments.slice(0, 5).map(comment => (
{comment.body}
{' • '}
@@ -39,5 +30,3 @@ function Comments(props, data) {
>
);
}
-
-export default block(Comments, load);
diff --git a/fixtures/blocks/src/server/FeedPage.block.js b/fixtures/blocks/src/server/Feed.js
similarity index 56%
rename from fixtures/blocks/src/server/FeedPage.block.js
rename to fixtures/blocks/src/server/Feed.js
index 82bfa57b4e..f00c5fff7e 100644
--- a/fixtures/blocks/src/server/FeedPage.block.js
+++ b/fixtures/blocks/src/server/Feed.js
@@ -7,29 +7,15 @@
/* eslint-disable import/first */
import * as React from 'react';
-import {unstable_block as block} from 'react';
import {fetch} from 'react-data/fetch';
-
-// Server
-
import PostList from './PostList';
-function load(params) {
- const allPosts = fetch('/posts?_expand=user').json();
- return {
- posts: ,
- };
-}
-
-// Client
-
-function FeedPage(props, data) {
+export default function Feed() {
+ const posts = fetch('/posts?_expand=user').json();
return (
<>
Feed
- {data.posts}
+
>
);
}
-
-export default block(FeedPage, load);
diff --git a/fixtures/blocks/src/server/FeedPage.js b/fixtures/blocks/src/server/FeedPage.js
new file mode 100644
index 0000000000..2eb33bda76
--- /dev/null
+++ b/fixtures/blocks/src/server/FeedPage.js
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+import {Suspense} from 'react';
+import PostGlimmer from './PostGlimmer';
+import Feed from './Feed';
+
+export default function FeedPage() {
+ return (
+ }>
+
+
+ );
+}
diff --git a/fixtures/blocks/src/server/Link.block.js b/fixtures/blocks/src/server/Link.block.js
new file mode 100644
index 0000000000..da8f36bbbc
--- /dev/null
+++ b/fixtures/blocks/src/server/Link.block.js
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+import {unstable_block as block} from 'react';
+
+// Client
+
+// TODO: delete this wrapper.
+
+import ClientLink from '../client/Link';
+
+function Link(props, _) {
+ return ;
+}
+
+export default block(Link);
diff --git a/fixtures/blocks/src/server/Post.block.js b/fixtures/blocks/src/server/Post.js
similarity index 56%
rename from fixtures/blocks/src/server/Post.block.js
rename to fixtures/blocks/src/server/Post.js
index 2d60cc874c..dc9fc10bc9 100644
--- a/fixtures/blocks/src/server/Post.block.js
+++ b/fixtures/blocks/src/server/Post.js
@@ -7,23 +7,14 @@
/* eslint-disable import/first */
import * as React from 'react';
-import {unstable_block as block, Suspense} from 'react';
+import {Suspense} from 'react';
+import Comments from './Comments';
-// Server
+// TODO: Replace with asset reference.
+import loadLink from './Link.block';
-import loadComments from './Comments.block';
-
-function load(postId) {
- return {
- Comments: loadComments(postId),
- };
-}
-
-// Client
-
-import Link from '../client/Link';
-
-function Post(props, data) {
+export default function Post({post}) {
+ const Link = loadLink();
return (
- {props.post.title}
+ {post.title}
{' by '}
-
- {props.post.user.name}
-
+ {post.user.name}
-
{props.post.body}
+
{post.body}
Loading comments...}
unstable_avoidThisFallback={true}>
-
+
);
}
-
-export default block(Post, load);
diff --git a/fixtures/blocks/src/server/PostList.js b/fixtures/blocks/src/server/PostList.js
index 561771c9ce..0efe90cd86 100644
--- a/fixtures/blocks/src/server/PostList.js
+++ b/fixtures/blocks/src/server/PostList.js
@@ -9,16 +9,14 @@
import * as React from 'react';
import {Suspense, unstable_SuspenseList as SuspenseList} from 'react';
import {preload} from 'react-data/fetch';
-
-import loadPost from './Post.block';
import PostGlimmer from './PostGlimmer';
+import Post from './Post';
export default function PostList({posts}) {
return (
{posts.map(post => {
preload(`/comments?postId=${post.id}&_expand=user`);
- const Post = loadPost(post.id);
return (
}>
diff --git a/fixtures/blocks/src/server/ProfileBio.block.js b/fixtures/blocks/src/server/ProfileBio.js
similarity index 51%
rename from fixtures/blocks/src/server/ProfileBio.block.js
rename to fixtures/blocks/src/server/ProfileBio.js
index 52332ecf25..90797864d3 100644
--- a/fixtures/blocks/src/server/ProfileBio.block.js
+++ b/fixtures/blocks/src/server/ProfileBio.js
@@ -7,28 +7,15 @@
/* eslint-disable import/first */
import * as React from 'react';
-import {unstable_block as block} from 'react';
-
-// Server
-
import {fetch} from 'react-data/fetch';
-function load(user) {
- return {
- user,
- bio: fetch(`/bios/${user.bioId}`).json(),
- };
-}
-
-// Client
-
-function ProfileBio(props, data) {
+export default function ProfileBio({userId}) {
+ const user = fetch(`/users/${userId}`).json();
+ const bio = fetch(`/bios/${user.bioId}`).json().text;
return (
<>
- {data.user.name}'s Bio
- {data.bio.text}
+ {user.name}'s Bio
+ {bio}
>
);
}
-
-export default block(ProfileBio, load);
diff --git a/fixtures/blocks/src/server/ProfileNav.block.js b/fixtures/blocks/src/server/ProfileNav.block.js
new file mode 100644
index 0000000000..794a1d85fa
--- /dev/null
+++ b/fixtures/blocks/src/server/ProfileNav.block.js
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+import {unstable_block as block} from 'react';
+
+// Client
+
+// TODO: delete this wrapper.
+
+import ClientProfileNav from '../client/ProfileNav';
+
+function ProfileNav(props, _) {
+ return ;
+}
+
+export default block(ProfileNav);
diff --git a/fixtures/blocks/src/server/ProfilePage.block.js b/fixtures/blocks/src/server/ProfilePage.block.js
deleted file mode 100644
index 757cfd0389..0000000000
--- a/fixtures/blocks/src/server/ProfilePage.block.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-/* eslint-disable import/first */
-
-import * as React from 'react';
-import {unstable_block as block, Suspense} from 'react';
-
-// Server
-
-import {fetch} from 'react-data/fetch';
-import loadProfileBio from './ProfileBio.block';
-import loadProfileTimeline from './ProfileTimeline.block';
-
-function load(userId, tab) {
- const user = fetch(`/users/${userId}`).json();
- let Tab;
- switch (tab) {
- case 'bio':
- Tab = loadProfileBio(user);
- break;
- default:
- Tab = loadProfileTimeline(userId);
- break;
- }
- return {
- Tab,
- user,
- };
-}
-
-// Client
-
-import {TabBar, TabLink} from '../client/TabNav';
-
-function ProfileTabNav({userId}) {
- // TODO: Don't hardcode ID.
- return (
-
- Timeline
- Bio
-
- );
-}
-
-function ProfilePage(props, data) {
- return (
- <>
- {data.user.name}
-
- Loading...}>
-
-
- >
- );
-}
-
-export default block(ProfilePage, load);
diff --git a/fixtures/blocks/src/server/ProfilePage.js b/fixtures/blocks/src/server/ProfilePage.js
new file mode 100644
index 0000000000..8aaaa2e6a1
--- /dev/null
+++ b/fixtures/blocks/src/server/ProfilePage.js
@@ -0,0 +1,35 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+import * as React from 'react';
+import {Suspense} from 'react';
+import {fetch} from 'react-data/fetch';
+import {matchRoute} from './ServerRouter';
+import ProfileTimeline from './ProfileTimeline';
+import ProfileBio from './ProfileBio';
+
+// TODO: Replace with asset reference.
+import loadProfileNav from './ProfileNav.block';
+
+// TODO: Router component?
+const ProfileRoutes = {
+ '/': props => ,
+ '/bio': props => ,
+};
+
+export default function ProfilePage(props) {
+ const user = fetch(`/users/${props.userId}`).json();
+ const ProfileNav = loadProfileNav();
+ const match = matchRoute(props, ProfileRoutes);
+ return (
+ <>
+ {user.name}
+
+ Loading...}>{match}
+ >
+ );
+}
diff --git a/fixtures/blocks/src/server/ProfileTimeline.block.js b/fixtures/blocks/src/server/ProfileTimeline.block.js
deleted file mode 100644
index 1597d518b8..0000000000
--- a/fixtures/blocks/src/server/ProfileTimeline.block.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-/* eslint-disable import/first */
-
-import * as React from 'react';
-import {unstable_block as block} from 'react';
-
-// Server
-
-import {fetch} from 'react-data/fetch';
-import PostList from './PostList';
-
-function load(userId) {
- const postsByUser = fetch(`/posts?userId=${userId}&_expand=user`).json();
- return {
- posts: ,
- };
-}
-
-// Client
-
-function ProfileTimeline(props, data) {
- return <>{data.posts}>;
-}
-
-export default block(ProfileTimeline, load);
diff --git a/fixtures/blocks/src/server/ProfileTimeline.js b/fixtures/blocks/src/server/ProfileTimeline.js
new file mode 100644
index 0000000000..96c0190f06
--- /dev/null
+++ b/fixtures/blocks/src/server/ProfileTimeline.js
@@ -0,0 +1,16 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+import {fetch} from 'react-data/fetch';
+import PostList from './PostList';
+
+export default function ProfileTimeline({userId}) {
+ const posts = fetch(`/posts?userId=${userId}&_expand=user`).json();
+ return ;
+}
diff --git a/fixtures/blocks/src/server/ServerRouter.js b/fixtures/blocks/src/server/ServerRouter.js
new file mode 100644
index 0000000000..c15345e2aa
--- /dev/null
+++ b/fixtures/blocks/src/server/ServerRouter.js
@@ -0,0 +1,54 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+function tryMatch(props, def) {
+ const defSegments = def.split('/').filter(Boolean);
+ const routeSegments = props.route.split('/').filter(Boolean);
+ let innerProps = {...props};
+ while (routeSegments.length > 0) {
+ if (defSegments.length === 0) {
+ return null;
+ }
+ const urlSegment = routeSegments.shift();
+ const defSegment = defSegments.shift();
+ if (urlSegment === defSegment) {
+ continue;
+ }
+ if (defSegment[0] === ':') {
+ innerProps[defSegment.slice(1)] = urlSegment;
+ continue;
+ }
+ if (defSegment === '*') {
+ innerProps.route = '/' + urlSegment + routeSegments.join('/');
+ return innerProps;
+ }
+ return null;
+ }
+ if (defSegments.length === 0) {
+ return innerProps;
+ }
+ if (defSegments.length === 1 && defSegments[0] === '*') {
+ innerProps.route = '/';
+ return innerProps;
+ }
+ return null;
+}
+
+export function matchRoute(props, defs) {
+ for (let def in defs) {
+ if (!defs.hasOwnProperty(def)) {
+ continue;
+ }
+ const innerProps = tryMatch(props, def);
+ if (innerProps) {
+ const match = defs[def](innerProps);
+ return match;
+ }
+ }
+ throw Error('Not found.');
+}
diff --git a/fixtures/blocks/src/server/Shell.block.js b/fixtures/blocks/src/server/Shell.block.js
new file mode 100644
index 0000000000..053fd88468
--- /dev/null
+++ b/fixtures/blocks/src/server/Shell.block.js
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+/* eslint-disable import/first */
+
+import * as React from 'react';
+import {unstable_block as block} from 'react';
+
+// Client
+
+// TODO: delete this wrapper.
+
+import ClientShell from '../client/Shell';
+
+function Shell(props, _) {
+ return ;
+}
+
+export default block(Shell);