From 87b3b418f77563129741517743b1a31b4275d60a Mon Sep 17 00:00:00 2001 From: ahaapple Date: Sun, 3 Nov 2024 22:03:42 +0800 Subject: [PATCH] Add blog couldflare-next-on-page-edge --- .../[locale]/(docs)/docs/[[...slug]]/page.tsx | 6 +- .../(marketing)/blog/[...slug]/page.tsx | 6 +- .../blog/en/couldflare-next-on-page-edge.mdx | 133 ++++++++++++++++++ .../blog/zh/couldflare-next-on-page-edge.mdx | 125 ++++++++++++++++ 4 files changed, 264 insertions(+), 6 deletions(-) create mode 100644 frontend/content/blog/en/couldflare-next-on-page-edge.mdx create mode 100644 frontend/content/blog/zh/couldflare-next-on-page-edge.mdx diff --git a/frontend/app/[locale]/(docs)/docs/[[...slug]]/page.tsx b/frontend/app/[locale]/(docs)/docs/[[...slug]]/page.tsx index 9de65a5f..811683a2 100644 --- a/frontend/app/[locale]/(docs)/docs/[[...slug]]/page.tsx +++ b/frontend/app/[locale]/(docs)/docs/[[...slug]]/page.tsx @@ -12,7 +12,7 @@ import '@/styles/mdx.css'; import { Metadata } from 'next'; import { absoluteUrl, cn } from '@/lib/utils'; -import { siteConfig } from '@/config'; +import { PageGenUrl, siteConfig } from '@/config'; import { type Locale, routing } from '@/i18n/routing'; import { unstable_setRequestLocale } from 'next-intl/server'; import Link from 'next/link'; @@ -112,8 +112,8 @@ export default async function DocPage({ params }: DocPageProps) { Hybrid AI Search Now - - AI Generate UI Now + + AI Page Generator Now diff --git a/frontend/app/[locale]/(marketing)/blog/[...slug]/page.tsx b/frontend/app/[locale]/(marketing)/blog/[...slug]/page.tsx index 2a347f07..9b1bcb9d 100644 --- a/frontend/app/[locale]/(marketing)/blog/[...slug]/page.tsx +++ b/frontend/app/[locale]/(marketing)/blog/[...slug]/page.tsx @@ -10,7 +10,7 @@ import Link from 'next/link'; import { absoluteUrl, cn, formatDate } from '@/lib/utils'; import { buttonVariants } from '@/components/ui/button'; -import { siteConfig } from '@/config'; +import { PageGenUrl, siteConfig } from '@/config'; import { unstable_setRequestLocale } from 'next-intl/server'; import { type Locale, routing } from '@/i18n/routing'; @@ -110,8 +110,8 @@ export default async function PostPage({ params }: PostPageProps) { Hybrid AI Search Now - - AI Generate UI Now + + AI Page Generator Now diff --git a/frontend/content/blog/en/couldflare-next-on-page-edge.mdx b/frontend/content/blog/en/couldflare-next-on-page-edge.mdx new file mode 100644 index 00000000..45056281 --- /dev/null +++ b/frontend/content/blog/en/couldflare-next-on-page-edge.mdx @@ -0,0 +1,133 @@ +--- +title: How to migrate MemFree from vercel to cloudflare next-on-pages +description: Issues and solutions for migrating MemFree from vercel to cloudflare next-on-pages +image: /images/blog/blog-post-3.jpg +date: '2024-11-03' +--- + +## Deploying a Static Next.js App on Cloudflare Pages + +Deploying a static Next.js application on Cloudflare Pages is straightforward. You can accomplish this with just a few commands by following the guide on [Cloudflare's documentation for deploying a static Next.js site](https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-static-nextjs-site/). + +To validate this process, I first extracted the React review feature from the MemFree AI Page Generator and deployed it as a standalone webpage: [React Shadcn UI Preview](https://reactshadcn.com/). + +The entire deployment process was smooth, with minimal obstacles encountered. + +## Deploying MemFree AI Page Generator on Cloudflare + +You can experience the online version here: [PageGen - AI Page Generator](https://pagegen.ai/). + +1. Challenge 1: All APIs Support for Edge Runtime + +2. Challenge 2: All Dynamic Pages Support for Edge Runtime + +## Specific Cloudflare Page Edge Compatibility Issues + +### 1. Custom Format Replacement for `util.format` + +To ensure compatibility with the edge environment, replace the `util.format` function with the following implementation: + +```ts +// Replace the util.format function with this one; this method works in the edge environment +export function format(template, ...args) { + if (args.length === 0) return template; + + if (args.length === 1 && typeof args[0] === 'object') { + return template.replace(/%[sdj]/g, (match) => String(args[0])); + } + + let index = 0; + return template.replace(/%[sdj]/g, () => { + if (index >= args.length) return ''; + const arg = args[index++]; + return String(arg); + }); +} +``` + +### 2. Replace `@upstash/redis` with `@upstash/redis/cloudflare` + +Using `@upstash/redis` directly may result in the following error: + +```ts +Failed to get searches: Error: The 'cache' field on 'RequestInitializerDict' is not implemented +``` + +To resolve this, simply replace the import statement: + +```ts +import { Redis } from '@upstash/redis/cloudflare'; +``` + +No other code modifications are necessary. + +### 3. Replace `pdfjs` with `unpdf` + +The `pdfjs` library, widely used for PDF parsing, is incompatible with the edge environment. If you attempt to use `pdfjs`, you may encounter the following errors during deployment: + +```ts +⚡️ Unexpected error: Build failed with 4 errors: +⚡️ :883:64363: ERROR: Could not resolve "fs" +⚡️ :883:64423: ERROR: Could not resolve "http" +⚡️ :883:64450: ERROR: Could not resolve "https" +⚡️ :883:64476: ERROR: Could not resolve "url" +``` + +The reason for these errors is that the edge environment does not support certain Node.js modules. The solution is to use [unpdf](https://github.com/unjs/unpdf) instead. + +### 4. Replace `uploadthing` with R2 + +`uploadthing` is a component designed to simplify client-side file uploads across various frameworks, but it does not support the Cloudflare edge environment. + +When using Cloudflare Pages or Workers, you can easily upload files to R2 with just a few lines of code, benefiting from free CDN acceleration: + +```ts +const res = await getRequestContext().env.IMAGES.put(safeFileName, file); +``` + +### 5. Use Static Variables Instead of `NEXT_PUBLIC` Environment Variables + +In Cloudflare's Next-on-Pages, `NEXT_PUBLIC` environment variables cannot be accessed directly. You can replace them with static variables instead. + +### 6. Replace `contentlayer2` with `next/mdx` + +The documentation and blog sections of MemFree were generated using `contentlayer2` based on MDX files. However, using `contentlayer2` in Cloudflare Next-on-Pages results in the following error: + +```ts +EvalError: Code generation from strings disallowed for this context +``` + +This limitation is unreasonable, as static pages can be fully generated during the build process without needing runtime generation. Ultimately, I replaced `contentlayer2` with `next/mdx`, allowing for complete static page generation during compilation. + +For more details, refer to [Markdown and MDX](https://nextjs.org/docs/pages/building-your-application/configuring/mdx). + +You can see the results on PageGen's [Privacy Policy](https://pagegen.ai/privacy) and [Terms of Service](https://pagegen.ai/terms) pages. + +### 7. Use Bundle Analyzer to Reduce Bundle Size + +One issue arises when enabling edge runtime for dynamic pages, as some large client dependencies may inadvertently enter the runtime's dependency tree: + +```ts +│ __next-on-pages-dist__/functions/search/[id].func.js │ esm │ 8995.45 KiB │ +``` + +For instance, the size of the search page can reach around 8 MB. By analyzing the bundle, we identified two large client packages: `@babel/standalone` and `heic2any`. + +We addressed this by dynamically importing these packages, enabling lazy loading and singleton instantiation, which reduced the overall project size from 25 MB to just 2 MB. + +### 8. Use Cloudflare Image Loader + +Integrating Cloudflare's Image Loader is straightforward. Follow the steps outlined in [Cloudflare Image Transform integration with Next.js](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/). The core steps include: + +1. Defining `imageLoader.ts` in your code. +2. Enabling Cloudflare's image transformation service for your domain [Cloudflare Image Transform Image](https://developers.cloudflare.com/images/get-started/). + +### 9. Domain Binding and Redirection + +The domain binding and redirection settings differ between Cloudflare Pages and Vercel. For a Pages project, you need to bind both the www and root domains to the Pages project. Then, use Cloudflare's built-in redirection rules to redirect the www domain to the root domain. + +## Online Experience + +You can explore the live version here: [PageGen - AI Page Generator](https://pagegen.ai/). + +All source code for MemFree is open-source, and contributions are welcome! Feel free to give it a star on GitHub: [MemFree GitHub](https://github.com/memfreeme/memfree). diff --git a/frontend/content/blog/zh/couldflare-next-on-page-edge.mdx b/frontend/content/blog/zh/couldflare-next-on-page-edge.mdx new file mode 100644 index 00000000..fa879c30 --- /dev/null +++ b/frontend/content/blog/zh/couldflare-next-on-page-edge.mdx @@ -0,0 +1,125 @@ +--- +title: How to migrate MemFree from vercel to cloudflare next-on-pages +description: Issues and solutions for migrating MemFree from vercel to cloudflare next-on-pages +image: /images/blog/blog-post-3.jpg +date: '2024-11-03' +--- + +## Deploy Static Nextjs App on cloudflare page is very easy + +你只需要参考 https://developers.cloudflare.com/pages/framework-guides/nextjs/deploy-a-static-nextjs-site/ 几行命令就可以搞定。 + +为了实践验证,我首先将 MemFree AI Page generator 中的react review 功能拆分出来,单独部署成一个网页: [React Shadcn UI Preview](https://reactshadcn.com/) + +整个过程十分流程,基本上没有阻碍 + +## 将 MemFree AI Page Generator 部署到 cloudflare + +### 挑战一:API 需要支持 edge runtime + +### 挑战二:动态页面需要支持 edge runtime + +## 具体的 edge 兼容性问题 + +### 1 自定义的 format 替换 util.format + +```ts +// Replace the util.format function with this one, this method could work on edge environment +export function format(template, ...args) { + if (args.length === 0) return template; + + if (args.length === 1 && typeof args[0] === 'object') { + return template.replace(/%[sdj]/g, (match) => String(args[0])); + } + + let index = 0; + return template.replace(/%[sdj]/g, () => { + if (index >= args.length) return ''; + const arg = args[index++]; + return String(arg); + }); +} +``` + +### 2 @upstash/redis/cloudflare 替换 @upstash/redis + +直接使用 @upstash/redis 会得到下面的报错: + +```ts +Failed to get searches: Error: The 'cache' field on 'RequestInitializerDict' is not implemented +``` + +只需要在 import 中用 @upstash/redis/cloudflare 替换 @upstash/redis, 其他代码都不需要改 + +```ts +import { Redis } from '@upstash/redis/cloudflare'; +``` + +### 3 unpdf 替换 pdfjs dist + +pdfjs 是一个使用比较广泛的 PDF 解析库,但是在edge 环境无法运行。 使用 pdfjs, 在执行 +`bun run deploy` 时,你会得到下面的报错: + +```ts +⚡️ Unexpected error: Build failed with 4 errors: +⚡️ :883:64363: ERROR: Could not resolve "fs" +⚡️ :883:64423: ERROR: Could not resolve "http" +⚡️ :883:64450: ERROR: Could not resolve "https" +⚡️ :883:64476: ERROR: Could not resolve "url" +``` + +原因是 edge 环境不支持对应node 模块。 + +解决方案是使用 [unpdf](https://github.com/unjs/unpdf) + +### 4 R2 替换 uploadthing + +uploadthing 是一款简化客户端文件上传的组件,对各种流行的框架进行了封装,简化使用,但是部支持 Cloudflare edge 环境。 + +当使用 cloudflare 的page 或者worker 时,几行代码就可以将文件上传到 R2 上,并免费获得CDN 加速, 十分简单,性能也不错。 + +```ts +const res = await getRequestContext().env.IMAGES.put(safeFileName, file); +``` + +### 5 用静态变量替换 NEXT_PUBLIC env 变量 + +NEXT_PUBLIC 的 env 变量在 cloudflare next-on-pages 中无法直接访问,可以直接用静态变量替换 + +### 6 用 next/mdx 替换 contentlayer2 + +memfree的doc和blog 都是利用contentlayer2 基于mdx文件生成的静态页面, 使用 contentlayer2,在 cloudflare next-on-pages 中会得到下面的报错: + +```ts +EvalError: Code generation from strings disallowed for this context +``` + +但是这一点其实是不合理的,因为是静态页面完全可以在build期间全部生成,不需要运行时动态生成。 最后我使用 next/mdx 替换了 contentlayer2,在编译期间将 mdx 完全替换成静态页面。 + +大家可以参考 [Markdown and MDX](https://nextjs.org/docs/pages/building-your-application/configuring/mdx) + +具体的效果可以参考 PageGen 的 [PageGen Privacy Policy](https://pagegen.ai/privacy) 和 [PageGen Terms of Service](https://pagegen.ai/terms) 页面。 + +### 7 利用 bundle-analyzer 减小打包大小 + +这里会出问题的原因时,当你对一些动态页面开启edge runtime时,一些很大的客户端依赖进入了runtime的依赖。 + +```ts +│ __next-on-pages-dist__/functions/search/[id].func.js │ esm │ 8995.45 KiB │ +``` + +例如上面这个示例,一个search 页面大小显示 8 M 左右,经过bundle-analyzer 分区edge 环境的包依赖后,就会两个很大的客户端包:`@babel/standalone` 和 `heic2any`。 + +然后我们就这两个包进行动态导入,延迟加载,单例化,整个项目的总体积直接从25M降低到2M。 + +### 8 使用 cloudflare Image Loader + +这个比较简单,参考 [Cloudflare Image Transform integrate nextjs](https://developers.cloudflare.com/images/transform-images/integrate-with-frameworks/) 进行处理就行。 核心就是2步: + +1. 代码中定义 `imageLoader.ts` +2. 对域名启用 Cloudflare image transformation 服务 [Cloudflare Image Transform Image](https://developers.cloudflare.com/images/get-started/) + +### 9 域名绑定和重定向 + +cloudflare page和vercel的域名绑定,重定向设置不同。 +对于一个page 项目,我们需要将 带www的域名和根域名都绑定到 page 项目,然后利用couldflare 自带的重定向规则,将www的域名 重定向到根域名。