aboutsummaryrefslogtreecommitdiff
path: root/examples/hello-next
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-08-20 13:17:57 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2021-08-20 13:17:57 -0700
commit5db15b6ec7cd41cc15214f4448b064f4dd11f974 (patch)
tree4dff15a434b069e17c8cb16cca1a20e8a015212f /examples/hello-next
parentf1d3aade3b408ead1c1459eb9e0d90e90290d687 (diff)
downloadbun-5db15b6ec7cd41cc15214f4448b064f4dd11f974.tar.gz
bun-5db15b6ec7cd41cc15214f4448b064f4dd11f974.tar.zst
bun-5db15b6ec7cd41cc15214f4448b064f4dd11f974.zip
demos -> examples
Former-commit-id: 19a5d395bd41b0a0b854cdf749eb96149f91cbe1
Diffstat (limited to 'examples/hello-next')
-rw-r--r--examples/hello-next/.eslintrc.json3
-rw-r--r--examples/hello-next/.gitignore34
-rw-r--r--examples/hello-next/README.md34
-rw-r--r--examples/hello-next/bun-framework-next/client.development.tsx327
-rw-r--r--examples/hello-next/bun-framework-next/index.js1
-rw-r--r--examples/hello-next/bun-framework-next/next-server.tsx0
-rw-r--r--examples/hello-next/bun-framework-next/package.json79
-rw-r--r--examples/hello-next/bun-framework-next/page-loader.ts142
-rw-r--r--examples/hello-next/bun-framework-next/render.tsx0
-rw-r--r--examples/hello-next/bun-framework-next/renderDocument.tsx618
-rw-r--r--examples/hello-next/bun-framework-next/route-loader.ts0
-rw-r--r--examples/hello-next/bun-framework-next/server.development.tsx84
-rw-r--r--examples/hello-next/bun-framework-next/server.production.tsx0
-rw-r--r--examples/hello-next/bun-framework-next/tsconfig.json23
-rw-r--r--examples/hello-next/components/Title.tsx9
-rw-r--r--examples/hello-next/components/subtitle.tsx3
-rw-r--r--examples/hello-next/next-env.d.ts6
-rw-r--r--examples/hello-next/next.config.js3
-rw-r--r--examples/hello-next/package.json13
-rw-r--r--examples/hello-next/pages/_app.tsx7
-rw-r--r--examples/hello-next/pages/api/hello.tsx5
-rw-r--r--examples/hello-next/pages/foo/bar/third.tsx26
-rw-r--r--examples/hello-next/pages/index.tsx82
-rw-r--r--examples/hello-next/pages/posts/[id].tsx19
-rw-r--r--examples/hello-next/pages/second.tsx22
-rw-r--r--examples/hello-next/public/favicon.icobin0 -> 25931 bytes
-rw-r--r--examples/hello-next/public/vercel.svg4
-rw-r--r--examples/hello-next/styles/Home.module.css121
-rw-r--r--examples/hello-next/styles/globals.css16
-rw-r--r--examples/hello-next/tsconfig.json23
30 files changed, 1704 insertions, 0 deletions
diff --git a/examples/hello-next/.eslintrc.json b/examples/hello-next/.eslintrc.json
new file mode 100644
index 000000000..bffb357a7
--- /dev/null
+++ b/examples/hello-next/.eslintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "next/core-web-vitals"
+}
diff --git a/examples/hello-next/.gitignore b/examples/hello-next/.gitignore
new file mode 100644
index 000000000..1437c53f7
--- /dev/null
+++ b/examples/hello-next/.gitignore
@@ -0,0 +1,34 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+# vercel
+.vercel
diff --git a/examples/hello-next/README.md b/examples/hello-next/README.md
new file mode 100644
index 000000000..b12f3e33e
--- /dev/null
+++ b/examples/hello-next/README.md
@@ -0,0 +1,34 @@
+This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
+
+[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
+
+The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
diff --git a/examples/hello-next/bun-framework-next/client.development.tsx b/examples/hello-next/bun-framework-next/client.development.tsx
new file mode 100644
index 000000000..a4ef9bf96
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/client.development.tsx
@@ -0,0 +1,327 @@
+var onlyChildPolyfill = React.Children.only;
+React.Children.only = function (children) {
+ if (children && typeof children === "object" && children.length == 1) {
+ return onlyChildPolyfill(children[0]);
+ }
+
+ return onlyChildPolyfill(children);
+};
+
+globalThis.global = globalThis;
+import * as ReactDOM from "react-dom";
+import App from "next/app";
+import mitt, { MittEmitter } from "next/dist/shared/lib/mitt";
+import { RouterContext } from "next/dist/shared/lib/router-context";
+import Router, {
+ AppComponent,
+ AppProps,
+ delBasePath,
+ hasBasePath,
+ PrivateRouteInfo,
+} from "next/dist/shared/lib/router/router";
+
+import * as NextRouteLoader from "next/dist/client/route-loader";
+import { isDynamicRoute } from "next/dist/shared/lib/router/utils/is-dynamic";
+import {
+ urlQueryToSearchParams,
+ assign,
+} from "next/dist/shared/lib/router/utils/querystring";
+import { setConfig } from "next/dist/shared/lib/runtime-config";
+import {
+ getURL,
+ loadGetInitialProps,
+ NEXT_DATA,
+ ST,
+} from "next/dist/shared/lib/utils";
+// import { Portal } from "next/dist/client/portal";
+import initHeadManager from "next/dist/client/head-manager";
+import { HeadManagerContext } from "next/dist/shared/lib/head-manager-context";
+import PageLoader from "./page-loader";
+import measureWebVitals from "next/dist/client/performance-relayer";
+import { RouteAnnouncer } from "next/dist/client/route-announcer";
+import {
+ createRouter,
+ makePublicRouterInstance,
+} from "next/dist/client/router";
+import * as React from "react";
+export const emitter: MittEmitter<string> = mitt();
+
+declare global {
+ interface Window {
+ /* test fns */
+ __NEXT_HYDRATED?: boolean;
+ __NEXT_HYDRATED_CB?: () => void;
+
+ /* prod */
+ __NEXT_PRELOADREADY?: (ids?: (string | number)[]) => void;
+ __NEXT_DATA__: NEXT_DATA;
+ __NEXT_P: any[];
+ }
+}
+
+type RenderRouteInfo = PrivateRouteInfo & {
+ App: AppComponent;
+ scroll?: { x: number; y: number } | null;
+};
+type RenderErrorProps = Omit<RenderRouteInfo, "Component" | "styleSheets">;
+
+const data: typeof window["__NEXT_DATA__"] = JSON.parse(
+ document.getElementById("__NEXT_DATA__")!.textContent!
+);
+window.__NEXT_DATA__ = data;
+
+const {
+ props: hydrateProps,
+ err: hydrateErr,
+ page,
+ query,
+ buildId,
+ assetPrefix,
+ runtimeConfig,
+ dynamicIds,
+ isFallback,
+ locale,
+ locales,
+ domainLocales,
+ isPreview,
+} = data;
+
+const prefix: string = assetPrefix || "";
+
+setConfig({
+ serverRuntimeConfig: {},
+ publicRuntimeConfig: runtimeConfig || {},
+});
+
+let asPath: string = getURL();
+
+// make sure not to attempt stripping basePath for 404s
+if (hasBasePath(asPath)) {
+ asPath = delBasePath(asPath);
+}
+
+const pageLoader: PageLoader = new PageLoader(buildId, prefix, data.pages);
+
+const headManager: {
+ mountedInstances: Set<unknown>;
+ updateHead: (head: JSX.Element[]) => void;
+} = initHeadManager();
+const appElement: HTMLElement | null = document.getElementById("__next");
+
+let lastRenderReject: (() => void) | null;
+let webpackHMR: any;
+export let router: Router;
+let CachedApp: AppComponent, onPerfEntry: (metric: any) => void;
+
+export default function boot(EntryPointNamespace, loader) {
+ _boot(EntryPointNamespace).then(() => {});
+}
+
+class Container extends React.Component<{
+ fn: (err: Error, info?: any) => void;
+}> {
+ componentDidCatch(componentErr: Error, info: any) {
+ this.props.fn(componentErr, info);
+ }
+
+ componentDidMount() {
+ this.scrollToHash();
+
+ // We need to replace the router state if:
+ // - the page was (auto) exported and has a query string or search (hash)
+ // - it was auto exported and is a dynamic route (to provide params)
+ // - if it is a client-side skeleton (fallback render)
+ if (
+ router.isSsr &&
+ // We don't update for 404 requests as this can modify
+ // the asPath unexpectedly e.g. adding basePath when
+ // it wasn't originally present
+ page !== "/404" &&
+ page !== "/_error" &&
+ (isFallback ||
+ (data.nextExport &&
+ (isDynamicRoute(router.pathname) ||
+ location.search ||
+ process.env.__NEXT_HAS_REWRITES)) ||
+ (hydrateProps &&
+ hydrateProps.__N_SSG &&
+ (location.search || process.env.__NEXT_HAS_REWRITES)))
+ ) {
+ // update query on mount for exported pages
+ router.replace(
+ router.pathname +
+ "?" +
+ String(
+ assign(
+ urlQueryToSearchParams(router.query),
+ new URLSearchParams(location.search)
+ )
+ ),
+ asPath,
+ {
+ // @ts-ignore
+ // WARNING: `_h` is an internal option for handing Next.js
+ // client-side hydration. Your app should _never_ use this property.
+ // It may change at any time without notice.
+ _h: 1,
+ // Fallback pages must trigger the data fetch, so the transition is
+ // not shallow.
+ // Other pages (strictly updating query) happens shallowly, as data
+ // requirements would already be present.
+ shallow: !isFallback,
+ }
+ );
+ }
+ }
+
+ componentDidUpdate() {
+ this.scrollToHash();
+ }
+
+ scrollToHash() {
+ let { hash } = location;
+ hash = hash && hash.substring(1);
+ if (!hash) return;
+
+ const el: HTMLElement | null = document.getElementById(hash);
+ if (!el) return;
+
+ // If we call scrollIntoView() in here without a setTimeout
+ // it won't scroll properly.
+ setTimeout(() => el.scrollIntoView(), 0);
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
+
+let CachedComponent: React.ComponentType;
+
+const wrapApp =
+ (App: AppComponent) =>
+ (wrappedAppProps: Record<string, any>): JSX.Element => {
+ const appProps: AppProps = {
+ ...wrappedAppProps,
+ Component: CachedComponent,
+ err: hydrateErr,
+ router,
+ };
+ return (
+ <AppContainer>
+ <App {...appProps} />
+ </AppContainer>
+ );
+ };
+
+function AppContainer({
+ children,
+}: React.PropsWithChildren<{}>): React.ReactElement {
+ return (
+ <Container fn={(error) => <div>{JSON.stringify(error)}</div>}>
+ <RouterContext.Provider value={makePublicRouterInstance(router)}>
+ <HeadManagerContext.Provider value={headManager}>
+ {children}
+ </HeadManagerContext.Provider>
+ </RouterContext.Provider>
+ </Container>
+ );
+}
+
+async function _boot(EntryPointNamespace) {
+ NextRouteLoader.default.getClientBuildManifest = () => Promise.resolve({});
+
+ const PageComponent = EntryPointNamespace.default;
+
+ const appScripts = globalThis.__NEXT_DATA__.pages["/_app"];
+ if (appScripts && appScripts.length > 0) {
+ let appSrc;
+ for (let asset of appScripts) {
+ if (!asset.endsWith(".css")) {
+ appSrc = asset;
+ break;
+ }
+ }
+
+ if (appSrc) {
+ const AppModule = await import(appSrc);
+ console.assert(
+ AppModule.default,
+ appSrc + " must have a default export'd React component"
+ );
+
+ CachedApp = AppModule.default;
+ } else {
+ CachedApp = App;
+ }
+ }
+
+ router = createRouter(page, query, asPath, {
+ initialProps: hydrateProps,
+ pageLoader,
+ App: CachedApp,
+ Component: CachedComponent,
+ wrapApp,
+ err: null,
+ isFallback: Boolean(isFallback),
+ subscription: async (info, App, scroll) => {
+ return render(
+ Object.assign<
+ {},
+ Omit<RenderRouteInfo, "App" | "scroll">,
+ Pick<RenderRouteInfo, "App" | "scroll">
+ >({}, info, {
+ App,
+ scroll,
+ })
+ );
+ },
+ locale,
+ locales,
+ defaultLocale: "",
+ domainLocales,
+ isPreview,
+ });
+
+ globalThis.next.router = router;
+
+ ReactDOM.hydrate(
+ <TopLevelRender
+ App={CachedApp}
+ Component={PageComponent}
+ props={{ pageProps: hydrateProps }}
+ />,
+ document.querySelector("#__next")
+ );
+}
+
+function TopLevelRender({ App, Component, props, scroll }) {
+ return (
+ <AppContainer scroll={scroll}>
+ <App Component={Component} {...props}></App>
+ </AppContainer>
+ );
+}
+
+export function render(props) {
+ ReactDOM.render(
+ <TopLevelRender {...props} />,
+ document.querySelector("#__next")
+ );
+}
+
+export function renderError(e) {
+ ReactDOM.render(
+ <AppContainer>
+ <App Component={<div>UH OH!!!!</div>} pageProps={data.props}></App>
+ </AppContainer>,
+ document.querySelector("#__next")
+ );
+}
+
+globalThis.next = {
+ version: "11.1.0",
+ emitter,
+ render,
+ renderError,
+};
diff --git a/examples/hello-next/bun-framework-next/index.js b/examples/hello-next/bun-framework-next/index.js
new file mode 100644
index 000000000..10adb5205
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/index.js
@@ -0,0 +1 @@
+console.log("HI");
diff --git a/examples/hello-next/bun-framework-next/next-server.tsx b/examples/hello-next/bun-framework-next/next-server.tsx
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/next-server.tsx
diff --git a/examples/hello-next/bun-framework-next/package.json b/examples/hello-next/bun-framework-next/package.json
new file mode 100644
index 000000000..9f12a57a3
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/package.json
@@ -0,0 +1,79 @@
+{
+ "name": "bun-framework-next",
+ "version": "0.0.0-8",
+ "description": "",
+ "main": "package.json",
+ "framework": {
+ "static": "public",
+ "assetPrefix": "_next/",
+ "router": {
+ "dir": [
+ "pages",
+ "src/pages"
+ ],
+ "extensions": [
+ ".js",
+ ".ts",
+ ".tsx"
+ ]
+ },
+ "css": "onimportcss",
+ "development": {
+ "client": "client.development.tsx",
+ "server": "server.development.tsx",
+ "css": "onimportcss",
+ "define": {
+ "client": {
+ ".env": "NEXT_PUBLIC_",
+ "defaults": {
+ "process.env.__NEXT_TRAILING_SLASH": "false",
+ "process.env.NODE_ENV": "\"development\"",
+ "process.env.__NEXT_ROUTER_BASEPATH": "''",
+ "process.env.__NEXT_SCROLL_RESTORATION": "false",
+ "process.env.__NEXT_I18N_SUPPORT": "false",
+ "process.env.__NEXT_HAS_REWRITES": "false",
+ "process.env.__NEXT_ANALYTICS_ID": "null",
+ "process.env.__NEXT_OPTIMIZE_CSS": "false",
+ "process.env.__NEXT_CROSS_ORIGIN": "''",
+ "process.env.__NEXT_STRICT_MODE": "false",
+ "process.env.__NEXT_IMAGE_OPTS": "null"
+ }
+ },
+ "server": {
+ ".env": "NEXT_",
+ "defaults": {
+ "process.env.__NEXT_TRAILING_SLASH": "false",
+ "process.env.__NEXT_OPTIMIZE_FONTS": "false",
+ "process.env.NODE_ENV": "\"development\"",
+ "process.env.__NEXT_OPTIMIZE_IMAGES": "false",
+ "process.env.__NEXT_OPTIMIZE_CSS": "false",
+ "process.env.__NEXT_ROUTER_BASEPATH": "''",
+ "process.env.__NEXT_SCROLL_RESTORATION": "false",
+ "process.env.__NEXT_I18N_SUPPORT": "false",
+ "process.env.__NEXT_HAS_REWRITES": "false",
+ "process.env.__NEXT_ANALYTICS_ID": "null",
+ "process.env.__NEXT_CROSS_ORIGIN": "''",
+ "process.env.__NEXT_STRICT_MODE": "false",
+ "process.env.__NEXT_IMAGE_OPTS": "null",
+ "global": "globalThis",
+ "window": "undefined"
+ }
+ }
+ }
+ },
+ "production": {
+ "client": "client.production.tsx",
+ "server": "server.production.tsx",
+ "css": "onimportcss"
+ }
+ },
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "buffer": "^6.0.3",
+ "path": "^0.12.7"
+ }
+}
diff --git a/examples/hello-next/bun-framework-next/page-loader.ts b/examples/hello-next/bun-framework-next/page-loader.ts
new file mode 100644
index 000000000..03ee3e5ed
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/page-loader.ts
@@ -0,0 +1,142 @@
+import NextPageLoader from "next/dist/client/page-loader";
+import getAssetPathFromRoute from "next/dist/shared/lib/router/utils/get-asset-path-from-route";
+// import createRouteLoader from "./route-loader";
+
+function insertStyleSheet(url: string) {
+ if (document.querySelector(`link[href="${url}"]`)) {
+ return Promise.resolve();
+ }
+
+ return new Promise((resolve, reject) => {
+ const link = document.createElement("link");
+ link.rel = "stylesheet";
+
+ link.onload = () => resolve();
+ link.onerror = () => reject();
+
+ link.href = url;
+ document.head.appendChild(link);
+ });
+}
+
+export default class PageLoader extends NextPageLoader {
+ public routeLoader: RouteLoader;
+
+ constructor(_, __, pages) {
+ super(_, __);
+
+ // TODO: assetPrefix?
+ // this.routeLoader = {}; //createRouteLoader("");
+
+ // Rewrite the pages object to omit the entry script
+ // At this point, the entry point has been loaded so we don't want to do that again.
+ for (let name in pages) {
+ for (let i = 0; i < pages[name].length; i += 1) {
+ const lastDot = pages[name][i].lastIndexOf(".");
+ if (lastDot == -1) continue;
+ if (
+ pages[name][i].substring(lastDot - ".entry".length, lastDot) !==
+ ".entry"
+ )
+ continue;
+
+ pages[name][i] =
+ pages[name][i].substring(0, lastDot - ".entry".length) +
+ pages[name][i].substring(lastDot);
+ }
+ }
+
+ this.pages = pages;
+ this.pageList = Object.keys(this.pages);
+ }
+
+ pageList: string[];
+ pages: Record<string, string[]>;
+
+ getPageList() {
+ return this.pageList;
+ }
+
+ cssQueue = [];
+
+ onImportCSS = (event) => {
+ this.cssQueue.push(
+ insertStyleSheet(event.detail).then(
+ () => {},
+ () => {}
+ )
+ );
+ };
+
+ prefetch(route) {
+ return Promise.resolve({});
+ }
+
+ async loadPage(route: string): Promise<GoodPageCache> {
+ const assets =
+ this.pages[route] || this.pages[getAssetPathFromRoute(route)];
+
+ var src;
+ console.log(getAssetPathFromRoute(route), assets);
+ for (let asset of assets) {
+ if (!asset.endsWith(".css")) {
+ src = asset;
+ break;
+ }
+ }
+ console.assert(src, "Invalid or unknown route passed to loadPage");
+
+ document.removeEventListener("onimportcss", this.onImportCSS);
+ this.cssQueue.length = 0;
+ document.addEventListener("onimportcss", this.onImportCSS, {
+ passive: true,
+ });
+
+ try {
+ const res = await import(src);
+
+ if (this.cssQueue.length > 0) {
+ await Promise.all(this.cssQueue);
+ this.cssQueue.length = 0;
+ }
+
+ document.removeEventListener("onimportcss", this.onImportCSS);
+
+ if (this.cssQueue.length > 0) {
+ await Promise.all(this.cssQueue);
+
+ this.cssQueue.length = 0;
+ }
+
+ return {
+ page: res.default,
+ mod: res,
+ styleSheets: [],
+ __N_SSG: false,
+ __N_SSP: false,
+ };
+ } catch (exception) {
+ console.error({ exception });
+ }
+
+ // return this.routeLoader.loadRoute(route).then((res) => {
+ // debugger;
+ // if ("component" in res) {
+ // return {
+ // page: res.component,
+ // mod: res.exports,
+ // styleSheets: res.styles.map((o) => ({
+ // href: o.href,
+ // text: o.content,
+ // })),
+ // };
+ // }
+ // throw res.error;
+ // });
+ }
+
+ // not used in development!
+ // prefetch(route: string): Promise<void> {
+ // return this.routeLoader.prefetch(route);
+ // }
+}
diff --git a/examples/hello-next/bun-framework-next/render.tsx b/examples/hello-next/bun-framework-next/render.tsx
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/render.tsx
diff --git a/examples/hello-next/bun-framework-next/renderDocument.tsx b/examples/hello-next/bun-framework-next/renderDocument.tsx
new file mode 100644
index 000000000..67c89efc2
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/renderDocument.tsx
@@ -0,0 +1,618 @@
+import * as App from "next/app";
+import { AmpStateContext } from "next/dist/shared/lib/amp-context";
+import { HeadManagerContext } from "next/dist/shared/lib/head-manager-context";
+import Loadable from "next/dist/shared/lib/loadable";
+import { LoadableContext } from "next/dist/shared/lib/loadable-context";
+import { RouterContext } from "next/dist/shared/lib/router-context";
+import { NextRouter } from "next/dist/shared/lib/router/router";
+import {
+ AppType,
+ ComponentsEnhancer,
+ DocumentInitialProps,
+ DocumentProps,
+ DocumentType,
+ getDisplayName,
+ loadGetInitialProps,
+ NextComponentType,
+ RenderPage,
+ RenderPageResult,
+} from "next/dist/shared/lib/utils";
+import * as NextDocument from "next/document";
+import * as ReactDOMServer from "react-dom/server.browser";
+import * as url from "url";
+import * as React from "react";
+
+const dev = process.env.NODE_ENV === "development";
+
+type ParsedUrlQuery = Record<string, string | string[]>;
+
+const isJSFile = (file: string) =>
+ file.endsWith(".js") ||
+ file.endsWith(".mjs") ||
+ file.endsWith(".ts") ||
+ file.endsWith(".tsx");
+
+function getScripts(files: DocumentFiles) {
+ const { context, props } = this;
+ const {
+ assetPrefix,
+ buildManifest,
+ isDevelopment,
+ devOnlyCacheBusterQueryString,
+ disableOptimizedLoading,
+ } = context;
+ const normalScripts = files.allFiles.filter(isJSFile);
+ const lowPriorityScripts = buildManifest.lowPriorityFiles?.filter(isJSFile);
+
+ return [...normalScripts, ...lowPriorityScripts].map((file) => {
+ return (
+ <script
+ key={file}
+ src={`${encodeURI(file)}${devOnlyCacheBusterQueryString}`}
+ nonce={props.nonce}
+ async
+ crossOrigin={props.crossOrigin || process.env.__NEXT_CROSS_ORIGIN}
+ type="module"
+ />
+ );
+ });
+}
+
+// function fixLink(from: string) {
+// if (from.startsWith("/_next/http://") || from.startsWith("/_next/https://"))
+// return from.substring("/_next".length);
+// return from;
+// }
+
+// function cloneWithOverwrittenLink(element: React.ReactElement<any>) {
+// const props = { ...element.props };
+// if ("href" in element.props) {
+// props.href = fixLink(props.href);
+// }
+
+// if ("n-href" in element.props) {
+// props["n-href"] = fixLink(props["n-href"]);
+// }
+
+// if ("n-src" in element.props) {
+// props["n-src"] = fixLink(props["n-src"]);
+// }
+
+// if ("src" in element.props) {
+// props["src"] = fixLink(props.src);
+// }
+
+// return React.cloneElement(element, props);
+// }
+
+interface DomainLocale {
+ defaultLocale: string;
+ domain: string;
+ http?: true;
+ locales?: string[];
+}
+
+function renderDocument(
+ Document: DocumentType,
+ {
+ buildManifest,
+ docComponentsRendered,
+ props,
+ docProps,
+ pathname,
+ query,
+ buildId,
+ canonicalBase,
+ assetPrefix,
+ runtimeConfig,
+ nextExport,
+ autoExport,
+ isFallback,
+ dynamicImportsIds,
+ dangerousAsPath,
+ err,
+ dev,
+ ampPath,
+ ampState,
+ inAmpMode,
+ hybridAmp,
+ dynamicImports,
+ headTags,
+ gsp,
+ gssp,
+ customServer,
+ gip,
+ appGip,
+ unstable_runtimeJS,
+ unstable_JsPreload,
+ devOnlyCacheBusterQueryString,
+ scriptLoader,
+ locale,
+ locales,
+ defaultLocale,
+ domainLocales,
+ isPreview,
+ disableOptimizedLoading,
+ }: RenderOpts & {
+ props: any;
+ docComponentsRendered: DocumentProps["docComponentsRendered"];
+ docProps: DocumentInitialProps;
+ pathname: string;
+ query: ParsedUrlQuery;
+ dangerousAsPath: string;
+ ampState: any;
+ ampPath: string;
+ inAmpMode: boolean;
+ hybridAmp: boolean;
+ dynamicImportsIds: (string | number)[];
+ dynamicImports: string[];
+ headTags: any;
+ isFallback?: boolean;
+ gsp?: boolean;
+ gssp?: boolean;
+ customServer?: boolean;
+ gip?: boolean;
+ appGip?: boolean;
+ devOnlyCacheBusterQueryString: string;
+ scriptLoader: any;
+ isPreview?: boolean;
+ autoExport?: boolean;
+ }
+): string {
+ return (
+ "<!DOCTYPE html>" +
+ ReactDOMServer.renderToStaticMarkup(
+ <AmpStateContext.Provider value={ampState}>
+ {Document.renderDocument(Document, {
+ __NEXT_DATA__: {
+ props, // The result of getInitialProps
+ page: pathname, // The rendered page
+ query, // querystring parsed / passed by the user
+ buildId, // buildId is used to facilitate caching of page bundles, we send it to the client so that pageloader knows where to load bundles
+ assetPrefix: assetPrefix === "" ? undefined : assetPrefix, // send assetPrefix to the client side when configured, otherwise don't sent in the resulting HTML
+ runtimeConfig, // runtimeConfig if provided, otherwise don't sent in the resulting HTML
+ nextExport, // If this is a page exported by `next export`
+ autoExport, // If this is an auto exported page
+ isFallback,
+ dynamicIds:
+ dynamicImportsIds.length === 0 ? undefined : dynamicImportsIds,
+ err: err || undefined, // err: err ? serializeError(dev, err) : undefined, // Error if one happened, otherwise don't sent in the resulting HTML
+ gsp, // whether the page is getStaticProps
+ gssp, // whether the page is getServerSideProps
+ customServer, // whether the user is using a custom server
+ gip, // whether the page has getInitialProps
+ appGip, // whether the _app has getInitialProps
+ locale,
+ locales,
+ defaultLocale,
+ domainLocales,
+ isPreview,
+
+ pages: buildManifest.pages,
+ },
+ buildManifest,
+ docComponentsRendered,
+ dangerousAsPath,
+ canonicalBase,
+ ampPath,
+ inAmpMode,
+ isDevelopment: !!dev,
+ hybridAmp,
+ dynamicImports,
+ assetPrefix,
+ headTags,
+ unstable_runtimeJS,
+ unstable_JsPreload,
+ devOnlyCacheBusterQueryString,
+ scriptLoader,
+ locale,
+ disableOptimizedLoading,
+ ...docProps,
+ })}
+ </AmpStateContext.Provider>
+ )
+ );
+}
+
+class ServerRouter implements NextRouter {
+ route: string;
+ pathname: string;
+ query: ParsedUrlQuery;
+ asPath: string;
+ basePath: string;
+ events: any;
+ isFallback: boolean;
+ locale?: string;
+ isReady: boolean;
+ locales?: string[];
+ defaultLocale?: string;
+ domainLocales?: DomainLocale[];
+ isPreview: boolean;
+ isLocaleDomain: boolean;
+
+ constructor(
+ pathname: string,
+ query: ParsedUrlQuery,
+ as: string,
+ { isFallback }: { isFallback: boolean },
+ isReady: boolean,
+ basePath: string,
+ locale?: string,
+ locales?: string[],
+ defaultLocale?: string,
+ domainLocales?: DomainLocale[],
+ isPreview?: boolean,
+ isLocaleDomain?: boolean
+ ) {
+ this.route = pathname.replace(/\/$/, "") || "/";
+ this.pathname = pathname;
+ this.query = query;
+ this.asPath = as;
+ this.isFallback = isFallback;
+ this.basePath = basePath;
+ this.locale = locale;
+ this.locales = locales;
+ this.defaultLocale = defaultLocale;
+ this.isReady = isReady;
+ this.domainLocales = domainLocales;
+ this.isPreview = !!isPreview;
+ this.isLocaleDomain = !!isLocaleDomain;
+ }
+
+ push(): any {
+ noRouter();
+ }
+ replace(): any {
+ noRouter();
+ }
+ reload() {
+ noRouter();
+ }
+ back() {
+ noRouter();
+ }
+ prefetch(): any {
+ noRouter();
+ }
+ beforePopState() {
+ noRouter();
+ }
+}
+
+function noRouter() {
+ const message =
+ 'No router instance found. you should only use "next/router" inside the client side of your app. https://nextjs.org/docs/messages/no-router-instance';
+ throw new Error(message);
+}
+
+function enhanceComponents(
+ options: ComponentsEnhancer,
+ App: AppType,
+ Component: NextComponentType
+): {
+ App: AppType;
+ Component: NextComponentType;
+} {
+ // For backwards compatibility
+ if (typeof options === "function") {
+ return {
+ App,
+ Component: options(Component),
+ };
+ }
+
+ return {
+ App: options.enhanceApp ? options.enhanceApp(App) : App,
+ Component: options.enhanceComponent
+ ? options.enhanceComponent(Component)
+ : Component,
+ };
+}
+
+Object.defineProperty(NextDocument.Head.prototype, "getScripts", {
+ get() {
+ return getScripts;
+ },
+});
+Object.defineProperty(NextDocument.NextScript.prototype, "getScripts", {
+ get() {
+ return getScripts;
+ },
+});
+
+export async function render({
+ route,
+ PageNamespace,
+ AppNamespace,
+ appStylesheets = [],
+ pageStylesheets = [],
+ DocumentNamespace = null,
+ buildId,
+ routePaths = [],
+}: {
+ buildId: number;
+ route: any;
+ PageNamespace: { default: NextComponentType<any> };
+ AppNamespace: { default: NextComponentType<any> } | null;
+ DocumentNamespace: Object | null;
+ appStylesheets: string[];
+ pageStylesheets: string[];
+ routePaths: string[];
+}): Promise<Response> {
+ const { default: Component, getStaticProps = null } = PageNamespace || {};
+ const { default: AppComponent_ } = AppNamespace || {};
+ var query = Object.assign({}, route.query);
+
+ // These are reversed in our Router versus Next.js...mostly due to personal preference.
+ const pathname = "/" + route.name;
+ var asPath = route.pathname;
+ const pages = {};
+
+ for (let path of routePaths) {
+ const filePath = path.substring(
+ path.indexOf("_next/pages/") + "_next/pages".length
+ );
+ const name = filePath.substring(0, filePath.indexOf("."));
+ pages[name] = [path];
+ }
+
+ pages[pathname] = [route.scriptSrc, ...pageStylesheets];
+
+ if (appStylesheets.length > 0) {
+ if (pages["/_app"]) {
+ pages["/_app"].push(...appStylesheets);
+ } else {
+ pages["/_app"] = appStylesheets;
+ }
+ }
+
+ const AppComponent = AppComponent_ || App.default;
+ const Document =
+ (DocumentNamespace && DocumentNamespace.default) || NextDocument.default;
+ // Document.Html.prototype.getScripts = getScripts;
+ // }
+
+ const callMiddleware = async (method: string, args: any[], props = false) => {
+ let results: any = props ? {} : [];
+
+ if ((Document as any)[`${method}Middleware`]) {
+ let middlewareFunc = await (Document as any)[`${method}Middleware`];
+ middlewareFunc = middlewareFunc.default || middlewareFunc;
+
+ const curResults = await middlewareFunc(...args);
+ if (props) {
+ for (const result of curResults) {
+ results = {
+ ...results,
+ ...result,
+ };
+ }
+ } else {
+ results = curResults;
+ }
+ }
+ return results;
+ };
+
+ const headTags = (...args: any) => callMiddleware("headTags", args);
+
+ const isFallback = !!query.__nextFallback;
+ delete query.__nextFallback;
+ delete query.__nextLocale;
+ delete query.__nextDefaultLocale;
+
+ const isSSG = !!getStaticProps;
+ const isBuildTimeSSG = isSSG && false;
+ const defaultAppGetInitialProps =
+ App.getInitialProps === (App as any).origGetInitialProps;
+
+ const hasPageGetInitialProps = !!(Component as any).getInitialProps;
+ const pageIsDynamic = route.kind === "dynamic";
+ const isAutoExport = false;
+
+ if (isAutoExport || isFallback) {
+ // // remove query values except ones that will be set during export
+ // query = {
+ // ...(query.amp
+ // ? {
+ // amp: query.amp,
+ // }
+ // : {}),
+ // };
+ asPath = `${asPath}${
+ // ensure trailing slash is present for non-dynamic auto-export pages
+ asPath.endsWith("/") && asPath !== "/" && !pageIsDynamic ? "/" : ""
+ }`;
+ }
+
+ let head: JSX.Element[] = [
+ <meta charSet="utf-8" />,
+ <meta name="viewport" content="width=device-width" />,
+ ];
+
+ const nextExport = isAutoExport || isFallback;
+ const reactLoadableModules: string[] = [];
+ var scriptLoader = {};
+ const AppContainer = ({ children }: any) => (
+ <RouterContext.Provider value={router}>
+ {/* <AmpStateContext.Provider value={ampState}> */}
+ <HeadManagerContext.Provider
+ value={{
+ updateHead: (state) => {
+ head = state;
+ },
+ updateScripts: (scripts) => {
+ scriptLoader = scripts;
+ },
+ scripts: {},
+ mountedInstances: new Set(),
+ }}
+ >
+ <LoadableContext.Provider
+ value={(moduleName) => reactLoadableModules.push(moduleName)}
+ >
+ {children}
+ </LoadableContext.Provider>
+ </HeadManagerContext.Provider>
+ {/* </AmpStateContext.Provider> */}
+ </RouterContext.Provider>
+ );
+
+ await Loadable.preloadAll(); // Make sure all dynamic imports are loaded
+
+ const router = new ServerRouter(
+ pathname,
+ query,
+ asPath,
+ {
+ isFallback: isFallback,
+ },
+ true,
+ Bun.origin,
+ null,
+ [], // renderOpts.locales,
+ null, //renderOpts.defaultLocale,
+ [], // renderOpts.domainLocales,
+ false,
+ false
+ );
+
+ const ctx = {
+ err: null,
+ req: undefined,
+ res: undefined,
+ pathname,
+ query,
+ asPath,
+ locale: null,
+ locales: [],
+ defaultLocale: null,
+ AppTree: (props: any) => {
+ return (
+ <AppContainer>
+ <App {...props} Component={Component} router={router} />
+ </AppContainer>
+ );
+ },
+ };
+
+ var props = await loadGetInitialProps(AppComponent, {
+ AppTree: ctx.AppTree,
+ Component,
+ router,
+ ctx,
+ });
+
+ const renderToString = ReactDOMServer.renderToString;
+ const ErrorDebug = null;
+
+ const renderPage: RenderPage = (
+ options: ComponentsEnhancer = {}
+ ): RenderPageResult | Promise<RenderPageResult> => {
+ if (ctx.err && ErrorDebug) {
+ const htmlOrPromise = renderToString(<ErrorDebug error={ctx.err} />);
+ return typeof htmlOrPromise === "string"
+ ? { html: htmlOrPromise, head }
+ : htmlOrPromise.then((html) => ({
+ html,
+ head,
+ }));
+ }
+
+ if (dev && (props.router || props.Component)) {
+ throw new Error(
+ `'router' and 'Component' can not be returned in getInitialProps from _app.js https://nextjs.org/docs/messages/cant-override-next-props`
+ );
+ }
+
+ const { App: EnhancedApp, Component: EnhancedComponent } =
+ enhanceComponents(options, AppComponent, Component);
+
+ const htmlOrPromise = renderToString(
+ <AppContainer>
+ <EnhancedApp Component={EnhancedComponent} router={router} {...props} />
+ </AppContainer>
+ );
+ return typeof htmlOrPromise === "string"
+ ? { html: htmlOrPromise, head }
+ : htmlOrPromise.then((html) => ({
+ html,
+ head,
+ }));
+ };
+
+ const documentCtx = { ...ctx, renderPage };
+ const docProps: DocumentInitialProps = await loadGetInitialProps(
+ Document,
+ documentCtx
+ );
+
+ if (!docProps || typeof docProps.html !== "string") {
+ const message = `"${getDisplayName(
+ Document
+ )}.getInitialProps()" should resolve to an object with a "html" prop set with a valid html string`;
+ throw new Error(message);
+ }
+
+ const renderOpts = {
+ params: route.params,
+ };
+ // renderOpts.params = _params || params;
+
+ // parsedUrl.pathname = denormalizePagePath(parsedUrl.pathname!);
+ // renderOpts.resolvedUrl = formatUrl({
+ // ...parsedUrl,
+ // query: origQuery,
+ // });
+ const docComponentsRendered: DocumentProps["docComponentsRendered"] = {};
+
+ const isPreview = false;
+ const getServerSideProps = PageNamespace.getServerSideProps;
+
+ let html = renderDocument(Document, {
+ docComponentsRendered,
+ ...renderOpts,
+ disableOptimizedLoading: false,
+ canonicalBase: Bun.origin,
+ buildManifest: {
+ devFiles: [],
+ allFiles: [],
+ polyfillFiles: [],
+ lowPriorityFiles: [],
+ pages: pages,
+ },
+ // Only enabled in production as development mode has features relying on HMR (style injection for example)
+ unstable_runtimeJS: true,
+ // process.env.NODE_ENV === "production"
+ // ? pageConfig.unstable_runtimeJS
+ // : undefined,
+ // unstable_JsPreload: pageConfig.unstable_JsPreload,
+ unstable_JsPreload: true,
+ dangerousAsPath: router.asPath,
+ ampState: undefined,
+ props,
+ assetPrefix: "",
+ headTags: await headTags(documentCtx),
+ isFallback,
+ docProps,
+ pathname,
+ ampPath: undefined,
+ query,
+ inAmpMode: false,
+ hybridAmp: undefined,
+ dynamicImportsIds: [], // Array.from(dynamicImportsIds),
+ dynamicImports: [], //Array.from(dynamicImports),
+ gsp: !!getStaticProps ? true : undefined,
+ gssp: !!getServerSideProps ? true : undefined,
+ gip: hasPageGetInitialProps ? true : undefined,
+ appGip: !defaultAppGetInitialProps ? true : undefined,
+ devOnlyCacheBusterQueryString: "",
+ scriptLoader,
+ isPreview: isPreview === true ? true : undefined,
+ autoExport: isAutoExport === true ? true : undefined,
+ nextExport: nextExport === true ? true : undefined,
+ })
+ .replaceAll("/_next/http://", "http://")
+ .replaceAll("/_next/https://", "https://");
+ return new Response(html);
+}
diff --git a/examples/hello-next/bun-framework-next/route-loader.ts b/examples/hello-next/bun-framework-next/route-loader.ts
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/route-loader.ts
diff --git a/examples/hello-next/bun-framework-next/server.development.tsx b/examples/hello-next/bun-framework-next/server.development.tsx
new file mode 100644
index 000000000..82caf0ab7
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/server.development.tsx
@@ -0,0 +1,84 @@
+import React from "react";
+class URL {
+ constructor(base, source) {
+ this.pathname = source;
+ this.href = base + source;
+ }
+}
+var onlyChildPolyfill = React.Children.only;
+React.Children.only = function (children) {
+ if (children && typeof children === "object" && children.length == 1) {
+ return onlyChildPolyfill(children[0]);
+ }
+
+ return onlyChildPolyfill(children);
+};
+globalThis.URL = URL;
+globalThis.global = globalThis;
+import { render } from "./renderDocument";
+
+let buildId = 0;
+
+var DocumentNamespacePromise;
+
+DocumentNamespacePromise = import(Bun.routesDir + "_document");
+var DocumentLoaded = false;
+var DocumentNamespace;
+
+addEventListener("fetch", async (event: FetchEvent) => {
+ if (!DocumentLoaded) {
+ DocumentLoaded = true;
+ try {
+ DocumentNamespace = await DocumentNamespacePromise;
+ } catch (exception) {
+ DocumentNamespace = null;
+ }
+ }
+
+ var appRoute;
+
+ try {
+ appRoute = await import(Bun.routesDir + "_app");
+ } catch (exception) {
+ appRoute = null;
+ }
+ const appStylesheets = (Bun.getImportedStyles() as string[]).slice();
+ var route = Bun.match(event);
+
+ // This imports the currently matched route.
+ const PageNamespace = await import(route.filePath);
+
+ // This returns all .css files that were imported in the line above.
+ // It's recursive, so any file that imports a CSS file will be included.
+ const pageStylesheets = (Bun.getImportedStyles() as string[]).slice();
+
+ event.respondWith(
+ render({
+ route,
+ PageNamespace,
+ appStylesheets,
+ pageStylesheets,
+ DocumentNamespace,
+ AppNamespace: appRoute,
+ buildId,
+ routePaths: Bun.getRouteFiles(),
+ })
+ );
+ buildId++;
+});
+
+// typescript isolated modules
+export {};
+
+declare var Bun: any;
+
+function getNextData(request: Request, route) {
+ return {
+ NEXT_DATA: {
+ query: route.query,
+ props: {},
+ page: route.path,
+ buildId: buildId.toString(16),
+ },
+ };
+}
diff --git a/examples/hello-next/bun-framework-next/server.production.tsx b/examples/hello-next/bun-framework-next/server.production.tsx
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/server.production.tsx
diff --git a/examples/hello-next/bun-framework-next/tsconfig.json b/examples/hello-next/bun-framework-next/tsconfig.json
new file mode 100644
index 000000000..679268d71
--- /dev/null
+++ b/examples/hello-next/bun-framework-next/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "lib": ["dom", "dom.iterable", "esnext", "WebWorker"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "baseUrl": ".",
+ "paths": {
+ "path": ["node_modules/path-browserify"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}
diff --git a/examples/hello-next/components/Title.tsx b/examples/hello-next/components/Title.tsx
new file mode 100644
index 000000000..c8f1a935f
--- /dev/null
+++ b/examples/hello-next/components/Title.tsx
@@ -0,0 +1,9 @@
+import Hey from "./subtitle";
+
+export default function Title() {
+ return (
+ <h1>
+ Hello <Hey></Hey>
+ </h1>
+ );
+}
diff --git a/examples/hello-next/components/subtitle.tsx b/examples/hello-next/components/subtitle.tsx
new file mode 100644
index 000000000..347d97a4d
--- /dev/null
+++ b/examples/hello-next/components/subtitle.tsx
@@ -0,0 +1,3 @@
+export default function Hey() {
+ return <div>!!yep</div>;
+}
diff --git a/examples/hello-next/next-env.d.ts b/examples/hello-next/next-env.d.ts
new file mode 100644
index 000000000..9bc3dd46b
--- /dev/null
+++ b/examples/hello-next/next-env.d.ts
@@ -0,0 +1,6 @@
+/// <reference types="next" />
+/// <reference types="next/types/global" />
+/// <reference types="next/image-types/global" />
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/examples/hello-next/next.config.js b/examples/hello-next/next.config.js
new file mode 100644
index 000000000..0d6071006
--- /dev/null
+++ b/examples/hello-next/next.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ reactStrictMode: true,
+}
diff --git a/examples/hello-next/package.json b/examples/hello-next/package.json
new file mode 100644
index 000000000..fd803c98a
--- /dev/null
+++ b/examples/hello-next/package.json
@@ -0,0 +1,13 @@
+{
+ "name": "hello-next",
+ "version": "1.0.0",
+ "main": "index.js",
+ "license": "MIT",
+ "dependencies": {
+ "next": "^11.1.0",
+ "path": "^0.12.7",
+ "react": "^17.0.2",
+ "react-dom": "^17.0.2",
+ "whatwg-url": "^9.1.0"
+ }
+}
diff --git a/examples/hello-next/pages/_app.tsx b/examples/hello-next/pages/_app.tsx
new file mode 100644
index 000000000..2fc3e0700
--- /dev/null
+++ b/examples/hello-next/pages/_app.tsx
@@ -0,0 +1,7 @@
+import "../styles/globals.css";
+
+function MyApp({ Component, pageProps }) {
+ return <Component {...pageProps} />;
+}
+
+export default MyApp;
diff --git a/examples/hello-next/pages/api/hello.tsx b/examples/hello-next/pages/api/hello.tsx
new file mode 100644
index 000000000..df63de88f
--- /dev/null
+++ b/examples/hello-next/pages/api/hello.tsx
@@ -0,0 +1,5 @@
+// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
+
+export default function handler(req, res) {
+ res.status(200).json({ name: 'John Doe' })
+}
diff --git a/examples/hello-next/pages/foo/bar/third.tsx b/examples/hello-next/pages/foo/bar/third.tsx
new file mode 100644
index 000000000..e1df24bc1
--- /dev/null
+++ b/examples/hello-next/pages/foo/bar/third.tsx
@@ -0,0 +1,26 @@
+import Link from "next/link";
+
+export default function Baz({}) {
+ return (
+ <div style={{ padding: 16 }}>
+ <h1>Third</h1>
+ <ul>
+ <li>
+ <Link href="/">
+ <a>Root page</a>
+ </Link>
+ </li>
+ <li>
+ <Link href="/second">
+ <a>Second page</a>
+ </Link>
+ </li>
+ <li>
+ <Link href="/posts/123">
+ <a>Post page 123</a>
+ </Link>
+ </li>
+ </ul>
+ </div>
+ );
+}
diff --git a/examples/hello-next/pages/index.tsx b/examples/hello-next/pages/index.tsx
new file mode 100644
index 000000000..b87c67a84
--- /dev/null
+++ b/examples/hello-next/pages/index.tsx
@@ -0,0 +1,82 @@
+import Head from "next/head";
+import Image from "next/image";
+import styles from "../styles/Home.module.css";
+import Link from "next/link";
+import { useRouter } from "next/router";
+
+import Title from "../components/Title";
+
+export default function Home() {
+ const router = useRouter();
+ return (
+ <div className={styles.container}>
+ <Head>
+ <title>Fo</title>
+ <meta name="description" content="Generated by create next app" />
+ <link rel="icon" href="/favicon.ico" />
+ </Head>
+
+ <Title />
+
+ <main className={styles.main}>
+ <h1 className={styles.title}>
+ Welcome to <a href="https://nextjs.org">Next.js!</a>
+ </h1>
+
+ <p className={styles.description}>
+ Get started by editing{" "}
+ <code className={styles.code}>pages/index.js</code>
+ </p>
+
+ <div className={styles.grid}>
+ <Link href="/second">
+ <div className={styles.card}>
+ <h2>Second Page &rarr;</h2>
+ <p>Link</p>
+ </div>
+ </Link>
+
+ <button
+ onClick={() => router.push("/foo/bar/third")}
+ className={styles.card}
+ style={{ backgroundColor: "white" }}
+ >
+ <h2>Third Page &rarr;</h2>
+ <p>button, router.push()</p>
+ </button>
+
+ <a
+ href="https://github.com/vercel/next.js/tree/master/examples"
+ className={styles.card}
+ >
+ <h2>Examples &rarr;</h2>
+ <p>Discover and deploy boilerplate example Next.js projects.</p>
+ </a>
+
+ <a
+ href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
+ className={styles.card}
+ >
+ <h2>Deploy &rarr;</h2>
+ <p>
+ Instantly deploy your Next.js site to a public URL with Vercel.
+ </p>
+ </a>
+ </div>
+ </main>
+
+ <footer className={styles.footer}>
+ <a
+ href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ Powered by{" "}
+ <span className={styles.logo}>
+ <img src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
+ </span>
+ </a>
+ </footer>
+ </div>
+ );
+}
diff --git a/examples/hello-next/pages/posts/[id].tsx b/examples/hello-next/pages/posts/[id].tsx
new file mode 100644
index 000000000..48413669c
--- /dev/null
+++ b/examples/hello-next/pages/posts/[id].tsx
@@ -0,0 +1,19 @@
+import { useRouter } from "next/router";
+import Link from "next/link";
+
+export default function Post({}) {
+ const router = useRouter();
+
+ return (
+ <div style={{ padding: 16 }}>
+ <h1>Post: {router.query.id}</h1>
+ <ul>
+ <li>
+ <Link href="/">
+ <a>Root page</a>
+ </Link>
+ </li>
+ </ul>
+ </div>
+ );
+}
diff --git a/examples/hello-next/pages/second.tsx b/examples/hello-next/pages/second.tsx
new file mode 100644
index 000000000..0e7b5a5cf
--- /dev/null
+++ b/examples/hello-next/pages/second.tsx
@@ -0,0 +1,22 @@
+import Link from "next/link";
+
+export default function Second({}) {
+ return (
+ <div style={{ padding: 16 }}>
+ <h1>Second</h1>
+
+ <ul>
+ <li>
+ <Link href="/">
+ <a>Root page</a>
+ </Link>
+ </li>
+ <li>
+ <Link href="/foo/bar/third">
+ <a>Third! page</a>
+ </Link>
+ </li>
+ </ul>
+ </div>
+ );
+}
diff --git a/examples/hello-next/public/favicon.ico b/examples/hello-next/public/favicon.ico
new file mode 100644
index 000000000..718d6fea4
--- /dev/null
+++ b/examples/hello-next/public/favicon.ico
Binary files differ
diff --git a/examples/hello-next/public/vercel.svg b/examples/hello-next/public/vercel.svg
new file mode 100644
index 000000000..fbf0e25a6
--- /dev/null
+++ b/examples/hello-next/public/vercel.svg
@@ -0,0 +1,4 @@
+<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
+ xmlns="http://www.w3.org/2000/svg">
+ <path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
+</svg> \ No newline at end of file
diff --git a/examples/hello-next/styles/Home.module.css b/examples/hello-next/styles/Home.module.css
new file mode 100644
index 000000000..35454bb74
--- /dev/null
+++ b/examples/hello-next/styles/Home.module.css
@@ -0,0 +1,121 @@
+.container {
+ min-height: 100vh;
+ padding: 0 0.5rem;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+}
+
+.main {
+ padding: 5rem 0;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+.footer {
+ width: 100%;
+ height: 100px;
+ border-top: 1px solid #eaeaea;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.footer a {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-grow: 1;
+}
+
+.title a {
+ color: #0070f3;
+ text-decoration: none;
+}
+
+.title a:hover,
+.title a:focus,
+.title a:active {
+ text-decoration: underline;
+}
+
+.title {
+ margin: 0;
+ line-height: 1.15;
+ font-size: 4rem;
+}
+
+.title,
+.description {
+ text-align: center;
+}
+
+.description {
+ line-height: 1.5;
+ font-size: 1.5rem;
+}
+
+.code {
+ background: #fafafa;
+ border-radius: 5px;
+ padding: 0.75rem;
+ font-size: 1.1rem;
+ font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
+ Bitstream Vera Sans Mono, Courier New, monospace;
+}
+
+.grid {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+ max-width: 800px;
+ margin-top: 3rem;
+}
+
+.card {
+ margin: 1rem;
+ padding: 1.5rem;
+ text-align: left;
+ color: inherit;
+ text-decoration: none;
+ border: 1px solid #eaeaea;
+ border-radius: 10px;
+ transition: color 0.15s ease, border-color 0.15s ease;
+ width: 45%;
+}
+
+.card:hover,
+.card:focus,
+.card:active {
+ color: #0070f3;
+ border-color: #0070f3;
+}
+
+.card h2 {
+ margin: 0 0 1rem 0;
+ font-size: 1.5rem;
+}
+
+.card p {
+ margin: 0;
+ font-size: 1.25rem;
+ line-height: 1.5;
+}
+
+.logo {
+ height: 1em;
+ margin-left: 0.5rem;
+}
+
+@media (max-width: 600px) {
+ .grid {
+ width: 100%;
+ flex-direction: column;
+ }
+}
diff --git a/examples/hello-next/styles/globals.css b/examples/hello-next/styles/globals.css
new file mode 100644
index 000000000..e5e2dcc23
--- /dev/null
+++ b/examples/hello-next/styles/globals.css
@@ -0,0 +1,16 @@
+html,
+body {
+ padding: 0;
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
+ Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+* {
+ box-sizing: border-box;
+}
diff --git a/examples/hello-next/tsconfig.json b/examples/hello-next/tsconfig.json
new file mode 100644
index 000000000..679268d71
--- /dev/null
+++ b/examples/hello-next/tsconfig.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "lib": ["dom", "dom.iterable", "esnext", "WebWorker"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": false,
+ "forceConsistentCasingInFileNames": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "Node",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "baseUrl": ".",
+ "paths": {
+ "path": ["node_modules/path-browserify"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
+ "exclude": ["node_modules"]
+}