@plasmicapp/nextjs-app-router 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,153 @@
1
+ This package provides helpers for doing extractPlasmicQueryData() with Next.js App Router.
2
+
3
+ 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.
4
+
5
+ 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.
6
+
7
+ So...
8
+
9
+ 1. First update you `plasmic.json` to use the `app` directory for pages and run `plasmic sync` again to automatically move the pages there:
10
+
11
+ ```
12
+ "nextjsConfig": {
13
+ "pagesDir": "../app"
14
+ },
15
+ ```
16
+
17
+ 2. Update `app/MyPage/page.tsx` to conditionally wrap the page contents inside `<ExtractPlasmicQueryData>` to extract the query data. It would look something like:
18
+
19
+ ```
20
+ // app/page.tsx
21
+
22
+ import * as React from "react";
23
+ import { PageParamsProvider as PageParamsProvider__ } from "@plasmicapp/react-web/lib/host";
24
+ import { PlasmicHomepage } from "../components/plasmic/my-plasmic-project/PlasmicHomepage";
25
+ import { PlasmicQueryDataProvider } from "@plasmicapp/react-web/lib/query";
26
+ import { ExtractPlasmicQueryData } from "@plasmicapp/nextjs-app-router";
27
+ import { fetchExtractedQueryData } from "@plasmicapp/nextjs-app-router/react-server";
28
+
29
+ // (Optional: In case you project uses Global Contexts)
30
+ import GlobalContextsProvider from "../components/plasmic/my-plasmic-project/PlasmicGlobalContextsProvider";
31
+
32
+ async function Homepage({
33
+ searchParams,
34
+ }: {
35
+ searchParams?: Record<string, string | string[]>;
36
+ }) {
37
+ return withExtractPlasmicQueryData(
38
+ <GlobalContextsProvider>
39
+ <PageParamsProvider__ route={"/"} query={searchParams}>
40
+ <PlasmicHomepage />
41
+ </PageParamsProvider__>
42
+ </GlobalContextsProvider>,
43
+ {
44
+ pathname: "/",
45
+ searchParams,
46
+ }
47
+ );
48
+ }
49
+
50
+ export default Homepage;
51
+
52
+ /**
53
+ * Helper function to extract Plasmic data.
54
+ *
55
+ * Given React element for your page and current pathname + search
56
+ * params, returns:
57
+ * - The extracted query data, if `plasmicSsr` search param is set
58
+ * - A copy of the page element wraped within PlasmicQueryDataProvider to provide the extracted query data, otherwise
59
+ */
60
+ async function withExtractPlasmicQueryData(
61
+ pageRootElt: React.ReactElement,
62
+ {
63
+ pathname,
64
+ searchParams,
65
+ }: {
66
+ pathname: string;
67
+ searchParams: Record<string, string | string[]> | undefined;
68
+ }
69
+ ) {
70
+ const isPlasmicSsr =
71
+ !!searchParams?.["plasmicSsr"] && searchParams?.["plasmicSsr"] !== "false";
72
+
73
+ // If `plasmicSsr` search param is set, just wrap the page inside
74
+ // <ExtractPlasmicQueryData>
75
+ if (isPlasmicSsr) {
76
+ return <ExtractPlasmicQueryData>{pageRootElt}</ExtractPlasmicQueryData>;
77
+ }
78
+
79
+ // Otherwise, fetch the same endpoint, but setting `plasmicSsr` to extract the
80
+ // query data.
81
+ const prepassHost =
82
+ process.env.PLASMIC_PREPASS_HOST ??
83
+ (process.env.VERCEL_URL && `https://${process.env.VERCEL_URL}`) ??
84
+ `http://localhost:${process.env.PORT ?? 3000}`;
85
+
86
+ // Build a copy of the search params
87
+ const newSearchParams = new URLSearchParams(
88
+ Object.entries(searchParams ?? {}).flatMap(([key, values]) =>
89
+ Array.isArray(values) ? values.map((v) => [key, v]) : [[key, values]]
90
+ )
91
+ );
92
+
93
+ // Set `plasmicSsr` search param to indicate you are using this endpoint
94
+ // to extract query data.
95
+ newSearchParams.set("plasmicSsr", "true");
96
+
97
+ if (process.env.VERCEL_AUTOMATION_BYPASS_SECRET) {
98
+ // If protection bypass is enabled, use it to ensure fetching from
99
+ // the SSR endpoint will not return the authentication page HTML
100
+ newSearchParams.set(
101
+ "x-vercel-protection-bypass",
102
+ process.env.VERCEL_AUTOMATION_BYPASS_SECRET
103
+ );
104
+ }
105
+
106
+ // Fetch the data from the endpoint using the new search params
107
+ const prefetchedQueryData = await fetchExtractedQueryData(
108
+ `${prepassHost}${pathname}?${newSearchParams.toString()}`
109
+ );
110
+
111
+ // Provide the query data to your page
112
+ return (
113
+ <PlasmicQueryDataProvider prefetchedCache={prefetchedQueryData}>
114
+ {pageRootElt}
115
+ </PlasmicQueryDataProvider>
116
+ );
117
+ }
118
+
119
+ ```
120
+
121
+ `<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. The helper function `withExtractPlasmicQueryData` will likely be moved into the package in the future.
122
+
123
+ Here, `fetchExtractedQueryData()` basically just hits the same endpoint with `?plasmicSsr=true`, and extracts the data from the json embedded in the `<script/>`.
124
+
125
+ 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.
126
+
127
+ 3. If you have static generation at build time, `@plasmicapp/nextjs-app-router` also comes with a `with-plasmic-prepass` command that you can use like this in your package.json:
128
+
129
+ ```
130
+ "script": {
131
+ "build": "with-plasmic-prepass -- next build"
132
+ }
133
+ ```
134
+
135
+ 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`.
136
+
137
+ 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`:
138
+
139
+ ```
140
+ module.exports = {
141
+ distDir: process.env.PLASMIC_PREPASS_SERVER ? ".next-prepass" : ".next"
142
+ }
143
+ ```
144
+
145
+ The `PLASMIC_PREPASS_SERVER` environment variable will be set by with-plasmic-prepass.
146
+
147
+ 4. If you are deploying to Vercel, make sure to either disable [Vercel Authentication](https://vercel.com/docs/security/deployment-protection/methods-to-protect-deployments/vercel-authentication) or provide the [Protection Bypass](https://vercel.com/docs/security/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation) (Note: this is a paid feature). This is to ensure the SSR endpoint will not return Vercel's page authentication HTML.
148
+
149
+ So...
150
+
151
+ - At dev time, uses itself for extracting query data (hits `localhost:${PORT}`)
152
+ - At build time, we start a parallel dev server via `with-plasmic-prepass`.
153
+ - In production, with revalidation, it will also use itself for extracting query data (using `VERCEL_URL` as the prepass host).
package/README.md CHANGED
@@ -100,6 +100,15 @@ async function withExtractPlasmicQueryData(
100
100
  // to extract query data.
101
101
  newSearchParams.set("plasmicSsr", "true");
102
102
 
103
+ if (process.env.VERCEL_AUTOMATION_BYPASS_SECRET) {
104
+ // If protection bypass is enabled, use it to ensure fetching from
105
+ // the SSR endpoint will not return the authentication page HTML
106
+ newSearchParams.set(
107
+ "x-vercel-protection-bypass",
108
+ process.env.VERCEL_AUTOMATION_BYPASS_SECRET
109
+ );
110
+ }
111
+
103
112
  // Fetch the data from the endpoint using the new search params
104
113
  const prefetchedQueryData = await fetchExtractedQueryData(
105
114
  `${prepassHost}${pathname}?${newSearchParams.toString()}`
@@ -138,6 +147,8 @@ module.exports = {
138
147
 
139
148
  The `PLASMIC_PREPASS_SERVER` environment variable will be set by with-plasmic-prepass.
140
149
 
150
+ 3. If you are deploying to Vercel, make sure to either disable [Vercel Authentication](https://vercel.com/docs/security/deployment-protection/methods-to-protect-deployments/vercel-authentication) or provide the [Protection Bypass](https://vercel.com/docs/security/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation) (Note: this is a paid feature). This is to ensure the SSR endpoint will not return Vercel's page authentication HTML.
151
+
141
152
  So...
142
153
 
143
154
  - At dev time, uses itself for extracting query data (hits `localhost:${PORT}`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasmicapp/nextjs-app-router",
3
- "version": "1.0.7",
3
+ "version": "1.0.9",
4
4
  "types": "./dist/index.d.ts",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.esm.js",
@@ -70,5 +70,5 @@
70
70
  "react": "^18.2.0",
71
71
  "typescript": "^5.2.2"
72
72
  },
73
- "gitHead": "352998d1e1e9ef88b49a892bd547058ee2e5e28f"
73
+ "gitHead": "2169c4a64fe85ab5c37d2a612fb37dccb86a1ca2"
74
74
  }