-
Notifications
You must be signed in to change notification settings - Fork 336
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3f8ebc0
commit f882878
Showing
21 changed files
with
2,709 additions
and
71 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
{ | ||
"name": "{{appName}}", | ||
"version": "1.0.0", | ||
"private": true, | ||
"scripts": { | ||
"build": "remix build", | ||
"dev": "remix dev --manual", | ||
"start": "remix-serve ./build/index.js", | ||
"typecheck": "tsc" | ||
}, | ||
"dependencies": { | ||
"@measured/puck": "{{puckVersion}}", | ||
"@remix-run/css-bundle": "^2.2.0", | ||
"@remix-run/node": "^2.2.0", | ||
"@remix-run/react": "^2.2.0", | ||
"@remix-run/serve": "^2.2.0", | ||
"isbot": "^3.6.8", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0" | ||
}, | ||
"devDependencies": { | ||
"@remix-run/dev": "^2.2.0", | ||
"@remix-run/eslint-config": "^2.2.0", | ||
"@types/react": "^18.2.20", | ||
"@types/react-dom": "^18.2.7", | ||
"eslint": "^8.38.0", | ||
"typescript": "^5.1.6" | ||
} | ||
} |
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,4 @@ | ||
/** @type {import('eslint').Linter.Config} */ | ||
module.exports = { | ||
extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"], | ||
}; |
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,6 @@ | ||
node_modules | ||
|
||
/.cache | ||
/build | ||
/public/build | ||
.env |
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,42 @@ | ||
# `remix` recipe | ||
|
||
The `remix` recipe showcases a Remix Run app with Puck, using it to provide an authoring tool for any root-level route in your Remix app. | ||
|
||
## Demonstrates | ||
|
||
- Remix Run V2 implementation | ||
- JSON database implementation with HTTP API | ||
- Dynamic routes to use puck for any root-level route on the platform | ||
- Option to disable client-side JavaScript for Puck pages | ||
|
||
## Usage | ||
|
||
Run the generator and enter `next` when prompted | ||
|
||
``` | ||
npx create-puck-app my-app | ||
``` | ||
|
||
Start the server | ||
|
||
``` | ||
yarn dev | ||
``` | ||
|
||
Navigate to the homepage at https://localhost:3000. To edit the homepage, access the Puck editor at https://localhost:3000/edit. | ||
|
||
You can do this for any **base** route on the application, **even if the page doesn't exist**. For example, visit https://localhost:3000/hello-world and you'll receive a 404. You can author and publish a page by visiting https://localhost:3000/hello-world/edit. After publishing, go back to the original URL to see your page. | ||
|
||
## Using this recipe | ||
|
||
To adopt this recipe you will need to: | ||
|
||
- **IMPORTANT** Add authentication to `/edit` routes. This can be done by modifying the example routes `/app/routes/_index.tsx` and `/app/routes/edit.tsx` or the example model in `/app/models/page.server.ts`. **If you don't do this, Puck will be completely public.** | ||
- Integrate your database into the API calls in `/app/models/page.server.ts` | ||
- Implement a custom puck configuration in `puck.config.tsx` | ||
|
||
By default, this recipe will have JavaScript enable on all routes - like a usual react app. If you know that your Puck content doesn't need react, then you can disable JS uncommenting the relevant code in `/app/root.tsx` and the example route `/app/routes/_index.tsx`. Check the network tab for no JS downloads, and verify that the page still works. | ||
|
||
## License | ||
|
||
MIT © [Measured Co.](https://github.com/measuredco) |
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,18 @@ | ||
/** | ||
* By default, Remix will handle hydrating your app on the client for you. | ||
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ | ||
* For more information, see https://remix.run/file-conventions/entry.client | ||
*/ | ||
|
||
import { RemixBrowser } from "@remix-run/react"; | ||
import { startTransition, StrictMode } from "react"; | ||
import { hydrateRoot } from "react-dom/client"; | ||
|
||
startTransition(() => { | ||
hydrateRoot( | ||
document, | ||
<StrictMode> | ||
<RemixBrowser /> | ||
</StrictMode> | ||
); | ||
}); |
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,137 @@ | ||
/** | ||
* By default, Remix will handle generating the HTTP Response for you. | ||
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨ | ||
* For more information, see https://remix.run/file-conventions/entry.server | ||
*/ | ||
|
||
import { PassThrough } from "node:stream"; | ||
|
||
import type { AppLoadContext, EntryContext } from "@remix-run/node"; | ||
import { createReadableStreamFromReadable } from "@remix-run/node"; | ||
import { RemixServer } from "@remix-run/react"; | ||
import isbot from "isbot"; | ||
import { renderToPipeableStream } from "react-dom/server"; | ||
|
||
const ABORT_DELAY = 5_000; | ||
|
||
export default function handleRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
remixContext: EntryContext, | ||
loadContext: AppLoadContext | ||
) { | ||
return isbot(request.headers.get("user-agent")) | ||
? handleBotRequest( | ||
request, | ||
responseStatusCode, | ||
responseHeaders, | ||
remixContext | ||
) | ||
: handleBrowserRequest( | ||
request, | ||
responseStatusCode, | ||
responseHeaders, | ||
remixContext | ||
); | ||
} | ||
|
||
function handleBotRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
remixContext: EntryContext | ||
) { | ||
return new Promise((resolve, reject) => { | ||
let shellRendered = false; | ||
const { pipe, abort } = renderToPipeableStream( | ||
<RemixServer | ||
context={remixContext} | ||
url={request.url} | ||
abortDelay={ABORT_DELAY} | ||
/>, | ||
{ | ||
onAllReady() { | ||
shellRendered = true; | ||
const body = new PassThrough(); | ||
const stream = createReadableStreamFromReadable(body); | ||
|
||
responseHeaders.set("Content-Type", "text/html"); | ||
|
||
resolve( | ||
new Response(stream, { | ||
headers: responseHeaders, | ||
status: responseStatusCode, | ||
}) | ||
); | ||
|
||
pipe(body); | ||
}, | ||
onShellError(error: unknown) { | ||
reject(error); | ||
}, | ||
onError(error: unknown) { | ||
responseStatusCode = 500; | ||
// Log streaming rendering errors from inside the shell. Don't log | ||
// errors encountered during initial shell rendering since they'll | ||
// reject and get logged in handleDocumentRequest. | ||
if (shellRendered) { | ||
console.error(error); | ||
} | ||
}, | ||
} | ||
); | ||
|
||
setTimeout(abort, ABORT_DELAY); | ||
}); | ||
} | ||
|
||
function handleBrowserRequest( | ||
request: Request, | ||
responseStatusCode: number, | ||
responseHeaders: Headers, | ||
remixContext: EntryContext | ||
) { | ||
return new Promise((resolve, reject) => { | ||
let shellRendered = false; | ||
const { pipe, abort } = renderToPipeableStream( | ||
<RemixServer | ||
context={remixContext} | ||
url={request.url} | ||
abortDelay={ABORT_DELAY} | ||
/>, | ||
{ | ||
onShellReady() { | ||
shellRendered = true; | ||
const body = new PassThrough(); | ||
const stream = createReadableStreamFromReadable(body); | ||
|
||
responseHeaders.set("Content-Type", "text/html"); | ||
|
||
resolve( | ||
new Response(stream, { | ||
headers: responseHeaders, | ||
status: responseStatusCode, | ||
}) | ||
); | ||
|
||
pipe(body); | ||
}, | ||
onShellError(error: unknown) { | ||
reject(error); | ||
}, | ||
onError(error: unknown) { | ||
responseStatusCode = 500; | ||
// Log streaming rendering errors from inside the shell. Don't log | ||
// errors encountered during initial shell rendering since they'll | ||
// reject and get logged in handleDocumentRequest. | ||
if (shellRendered) { | ||
console.error(error); | ||
} | ||
}, | ||
} | ||
); | ||
|
||
setTimeout(abort, ABORT_DELAY); | ||
}); | ||
} |
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,27 @@ | ||
import { Data } from "@measured/puck"; | ||
import fs from "fs"; | ||
|
||
// Replace with call to your database | ||
export const getPage = (path: string) => { | ||
const allData: Record<string, Data> | null = fs.existsSync("database.json") | ||
? JSON.parse(fs.readFileSync("database.json", "utf-8")) | ||
: null; | ||
|
||
return allData ? allData[path] : null; | ||
}; | ||
|
||
// Replace with call to your database | ||
export const setPage = (path: string, data: Data) => { | ||
const existingData = JSON.parse( | ||
fs.existsSync("database.json") | ||
? fs.readFileSync("database.json", "utf-8") | ||
: "{}" | ||
); | ||
|
||
const updatedData = { | ||
...existingData, | ||
[path]: data, | ||
}; | ||
|
||
fs.writeFileSync("database.json", JSON.stringify(updatedData)); | ||
}; |
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,43 @@ | ||
import { cssBundleHref } from "@remix-run/css-bundle"; | ||
import type { LinksFunction } from "@remix-run/node"; | ||
import { | ||
Links, | ||
LiveReload, | ||
Meta, | ||
Outlet, | ||
Scripts, | ||
ScrollRestoration, | ||
// useMatches, | ||
} from "@remix-run/react"; | ||
|
||
export const links: LinksFunction = () => [ | ||
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []), | ||
]; | ||
|
||
export default function App() { | ||
/** | ||
* Disable client-side JS - Optional | ||
* @see https://remix.run/docs/en/main/guides/disabling-javascript | ||
*/ | ||
// const matches = useMatches(); | ||
// const includeScripts = matches.some((match) => match.handle?.hydrate); | ||
|
||
return ( | ||
<html lang="en"> | ||
<head> | ||
<meta charSet="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<Meta /> | ||
<Links /> | ||
</head> | ||
<body> | ||
<Outlet /> | ||
<ScrollRestoration /> | ||
{/* Conditionally render scripts - Optional */} | ||
{/* {includeScripts ? <Scripts /> : null} */} | ||
<Scripts /> | ||
<LiveReload /> | ||
</body> | ||
</html> | ||
); | ||
} |
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 @@ | ||
export { default } from "./_index"; | ||
export * from "./_index"; |
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,5 @@ | ||
export { default } from "./edit"; | ||
// I think a bug in remix means loader needs to be explicitly exported here | ||
export { action, loader } from "./edit"; | ||
// For meta and links etc. | ||
export * from "./edit"; |
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,49 @@ | ||
import { Render, type Config } from "@measured/puck"; | ||
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; | ||
import { json } from "@remix-run/node"; | ||
import { useLoaderData } from "@remix-run/react"; | ||
|
||
import puckConfig from "../../puck.config"; | ||
import { getPage } from "~/models/page.server"; | ||
|
||
/** | ||
* Disable client-side JS - Optional | ||
* If you know that your Puck content doesn't need react. | ||
* Then you can disable JS for this route. | ||
* @see https://remix.run/docs/en/main/guides/disabling-javascript | ||
*/ | ||
|
||
// export const handle = { hydrate: false }; | ||
|
||
export const loader = async ({ params }: LoaderFunctionArgs) => { | ||
// Get path, and default to slash for root path. | ||
const puckPath = params.puckPath || "/"; | ||
// Get puckData for this path, this could be a database call. | ||
const puckData = getPage(puckPath); | ||
if (!puckData) { | ||
throw new Response(null, { | ||
status: 404, | ||
statusText: "Not Found", | ||
}); | ||
} | ||
// Return the data. | ||
return json({ puckData }); | ||
}; | ||
|
||
export const meta: MetaFunction<typeof loader> = ({ data }) => { | ||
const title = data?.puckData?.root?.title || "Page"; | ||
|
||
return [{ title }]; | ||
}; | ||
|
||
export default function Page() { | ||
const { puckData } = useLoaderData<typeof loader>(); | ||
|
||
/** | ||
* TypeStript error | ||
* Type 'Config<Props>' is not assignable to type 'Config'. Use 'as Config' for now. | ||
* @see https://github.com/measuredco/puck/issues/185 | ||
*/ | ||
|
||
return <Render config={puckConfig as Config} data={puckData} />; | ||
} |
Oops, something went wrong.
f882878
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
puck-docs – ./apps/docs
puck-docs-git-main-measured.vercel.app
puck-docs.vercel.app
puck-docs-measured.vercel.app
www.puckeditor.com
puckeditor.com