forked from plasmicapp/plasmic
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[rsc] Add @plasmicapp/nextjs-app-router
Change-Id: I31bdb522e5f7c6a95eca93ceab8e4548e3087334 GitOrigin-RevId: 64b590fc320e6e2081763981d39a27dd6fdd95af
- Loading branch information
1 parent
a0aebf4
commit eafdaff
Showing
19 changed files
with
508 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dist/ | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
This package provides helpers for doing extractPlasmicQueryData() with Next.js App Router. | ||
|
||
We normally use react-ssr-prepass to fake-render a React tree to gather data requirements. We can't do so in RSC mode, because all the client components are imported as placeholders, so we cannot fake-render them. | ||
|
||
The idea here is to use the dev server's SSR instead! At SSR time (instead of RSC time), we do have access to imported client components. So... we could do pre-rendering there, gather the data needs, and respond with them. At RSC time, we hit the SSR endpoint, and parse out the data needs. | ||
|
||
So... | ||
|
||
1. Create a `app/plasmic-ssr/[[...catchall]]/page.tsx` route, whose purpose is to perform SSR. It looks something like... | ||
|
||
``` | ||
import { ExtractPlasmicQueryData } from "@plasmicapp/nextjs-app-router"; | ||
export default async function CatchallPrepass(props: { | ||
params?: Params; | ||
}) { | ||
const { params } = props; | ||
const plasmicPath = params.catchall ? `/${params.catchall.join("/")}` : "/"; | ||
const prefetchedData = await PLASMIC.maybeFetchComponentData(plasmicPath); | ||
if (!prefetchedData || prefetchedData.entryCompMetas.length === 0) { | ||
notFound(); | ||
} | ||
const pageMeta = prefetchedData.entryCompMetas[0]; | ||
return ( | ||
<ExtractPlasmicQueryData> | ||
<PlasmicClientRootProvider | ||
prefetchedData={prefetchedData} | ||
pageParams={pageMeta.params} | ||
> | ||
<PlasmicComponent | ||
component={pageMeta.displayName} | ||
/> | ||
</PlasmicClientRootProvider> | ||
</ExtractPlasmicQueryData> | ||
) | ||
} | ||
``` | ||
|
||
`<ExtractPlasmicQueryData />` is a new client component from this package, which basically performs `extractPlasmicQueryData()` on its children, and then renders a `<script data-plasmic-prefetch-id/>` tag with the json of the extracted data. | ||
|
||
2. From the real `app/[...catchall]/page.tsx` file, make use of this endpoint to read the extracted data: | ||
|
||
``` | ||
import { fetchExtractedQueryData } from "@plasmicapp/nextjs-app-router"; | ||
export default async function Catchall(props: { | ||
params?: Params; | ||
}) { | ||
const { params } = props; | ||
const plasmicPath = params.catchall ? `/${params.catchall.join("/")}` : "/"; | ||
const prefetchedData = await PLASMIC.maybeFetchComponentData(plasmicPath); | ||
if (!prefetchedData || prefetchedData.entryCompMetas.length === 0) { | ||
notFound(); | ||
} | ||
const prepassHost = process.env.PLASMIC_PREPASS_HOST ?? process.env.VERCEL_URL ?? `http://localhost:${process.env.PORT ?? 3000}`; | ||
const queryData = await fetchExtractedQueryData(`${prepassHost}/plasmic-ssr/${(params?.catchall ?? []).join("/")}`); | ||
const pageMeta = prefetchedData.entryCompMetas[0]; | ||
return ( | ||
<PlasmicClientRootProvider | ||
prefetchedData={prefetchedData} | ||
prefetchedQueryData={queryData} | ||
pageParams={pageMeta.params} | ||
> | ||
<PlasmicComponent | ||
component={pageMeta.displayName} | ||
/> | ||
</PlasmicClientRootProvider> | ||
) | ||
} | ||
``` | ||
|
||
Here, `fetchExtractedQueryData()` basically just hits the `/plasmic-ssr/` endpoint, and extracts the data from the json embedded in the `<script/>`. | ||
|
||
The `prepassHost` to use is read from `PLASMIC_PREPASS_HOST` or `VERCEL_URL`. `VERCEL_URL` is available when your site is deployed on Vercel; it is the generated deployment url. | ||
|
||
`@plasmicapp/nextjs-app-router` also comes with a `with-plasmic-prepass` command that you can use like this in your package.json: | ||
|
||
``` | ||
"script": { | ||
"build": "with-plasmic-prepass -- next build" | ||
} | ||
``` | ||
|
||
This script will start up the next dev server at some random port (by running npm run dev), run the passed command, and then kill the dev server. It will run the command with the proper `PLASMIC_PREPASS_HOST` env variable, so the user never needs to think about it. You can choose to use a different package.json script command to start the dev server via `with-plasmic-prepass -c prepass -- next build`. | ||
|
||
Unfortunately another drawback is that the dev server and the build process will step on each other's toes, so you need to direct them to use different output folders. You do it in `next.config.js`: | ||
|
||
``` | ||
module.exports = { | ||
distDir: process.env.PLASMIC_PREPASS_SERVER ? ".next-prepass" : ".next" | ||
} | ||
``` | ||
|
||
The `PLASMIC_PREPASS_SERVER` environment variable will be set by with-plasmic-prepass. | ||
|
||
So... | ||
|
||
- At dev time, uses itself for extracting query data (hits `localhost:${PORT}`) | ||
- At build time, we start a parallel dev server. | ||
- In production, with revalidation, it will also use itself for extracting query data (using `VERCEL_URL` as the prepass host). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
## API Report File for "@plasmicapp/nextjs-app-router" | ||
|
||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). | ||
```ts | ||
|
||
import * as React_2 from 'react'; | ||
|
||
// @public | ||
export function ExtractPlasmicQueryData(props: { | ||
children?: React_2.ReactNode; | ||
}): React_2.JSX.Element | null; | ||
|
||
// @public (undocumented) | ||
export function fetchExtractedQueryData(url: string): Promise<any>; | ||
|
||
// (No @packageDocumentation comment for this package) | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
## API Report File for "@plasmicapp/nextjs-app-router" | ||
|
||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). | ||
```ts | ||
// @public (undocumented) | ||
export function fetchExtractedQueryData(url: string): Promise<any>; | ||
|
||
// (No @packageDocumentation comment for this package) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
{ | ||
"name": "@plasmicapp/nextjs-app-router", | ||
"version": "1.0.1", | ||
"types": "./dist/index.d.ts", | ||
"main": "./dist/index.js", | ||
"module": "./dist/index.esm.js", | ||
"files": [ | ||
"dist" | ||
], | ||
"bin": { | ||
"with-plasmic-prepass": "./dist/with-plasmic-prepass.cjs.js" | ||
}, | ||
"engines": { | ||
"node": ">=16" | ||
}, | ||
"scripts": { | ||
"build": "yarn build:types && yarn build:index && yarn build:react-server && yarn build:with-dev-server", | ||
"build:types": "yarn tsc", | ||
"build:index": "node ../../build.mjs ./src/index.ts --use-client", | ||
"build:with-dev-server": "esbuild --format=cjs --target=node18 --bundle --outfile=./dist/with-plasmic-prepass.cjs.js --platform=node ./src/with-dev-server.mts", | ||
"build:react-server": "node ../../build.mjs ./src/react-server.ts", | ||
"test": "yarn --cwd=../.. test", | ||
"coverage": "yarn --cwd=../.. test --coverage --passWithNoTests", | ||
"lint": "eslint", | ||
"prepare": "if-env PREPARE_NO_BUILD=true || yarn build" | ||
}, | ||
"dependencies": { | ||
"@plasmicapp/prepass": "1.0.14", | ||
"fkill": "^8.1.0", | ||
"get-port": "^7.0.0", | ||
"node-html-parser": "^6.1.5", | ||
"yargs": "^17.7.2" | ||
}, | ||
"peerDependencies": { | ||
"react": ">=16.8.0", | ||
"react-dom": ">=16.8.0" | ||
}, | ||
"license": "MIT", | ||
"exports": { | ||
".": { | ||
"types": "./dist/index.d.ts", | ||
"import": "./dist/index.esm.js", | ||
"require": "./dist/index.js" | ||
}, | ||
"./react-server": { | ||
"types": "./dist/react-server.d.ts", | ||
"import": "./dist/react-server.esm.js", | ||
"require": "./dist/react-server.js" | ||
}, | ||
"./react-server-conditional": { | ||
"react-server": { | ||
"types": "./dist/react-server.d.ts", | ||
"import": "./dist/react-server.esm.js", | ||
"require": "./dist/react-server.js" | ||
}, | ||
"default": { | ||
"types": "./dist/index.d.ts", | ||
"import": "./dist/index.esm.js", | ||
"require": "./dist/index.js" | ||
} | ||
} | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^20.8.9", | ||
"@types/react": "^18.0.27", | ||
"@types/yargs": "^17.0.32", | ||
"react": "^18.2.0", | ||
"typescript": "^5.2.2" | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
packages/nextjs-app-router/src/ExtractPlasmicQueryData.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { extractPlasmicQueryData } from "@plasmicapp/prepass"; | ||
import * as React from "react"; | ||
|
||
/** | ||
* EXPERIMENTAL | ||
* | ||
* A component that serves the same purpose as extractPlasmicQueryData(), but from | ||
* React server components. This only works from frameworks that support | ||
* React.useId() and React.use() (like Next.js 13). | ||
* | ||
* The children of this component will be run through `extractPlasmicQueryData()`. | ||
*/ | ||
export function ExtractPlasmicQueryData(props: { children?: React.ReactNode }) { | ||
const { children } = props; | ||
if (!React.useId || !(React as any).use) { | ||
throw new Error( | ||
`You can only use <ExtractPlasmicQueryData /> from server components.` | ||
); | ||
} | ||
const scriptId = `plasmic-prefetch-${React.useId()}`; | ||
console.log("SCRIPT ID", scriptId); | ||
if (typeof window === "undefined") { | ||
const data: Record<string, any> = (React as any).use( | ||
extractPlasmicQueryData(<>{children}</>) | ||
); | ||
return ( | ||
<> | ||
<script | ||
type="application/json" | ||
dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }} | ||
data-plasmic-prefetch-id={scriptId} | ||
suppressHydrationWarning={true} | ||
/> | ||
</> | ||
); | ||
} else { | ||
return null; | ||
} | ||
} |
Oops, something went wrong.