create-puck-app 0.11.1-canary.1a8f6b8 → 0.12.0-canary.5caf0ab

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,15 +1,21 @@
1
1
  {
2
2
  "name": "create-puck-app",
3
- "version": "0.11.1-canary.1a8f6b8",
3
+ "version": "0.12.0-canary.5caf0ab",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "bin": {
8
8
  "create-puck-app": "./index.js"
9
9
  },
10
+ "files": [
11
+ "templates",
12
+ "index.js"
13
+ ],
10
14
  "scripts": {
11
15
  "generate": "node scripts/generate.js",
12
- "prepublishOnly": "yarn generate"
16
+ "prepublishOnly": "yarn generate",
17
+ "removeGitignore": "mv templates/.gitignore templates/gitignore",
18
+ "restoreGitignore": "mv templates/gitignore templates/.gitignore"
13
19
  },
14
20
  "dependencies": {
15
21
  "commander": "^10.0.1",
@@ -0,0 +1,8 @@
1
+ # Ignore everything
2
+ *.*
3
+
4
+ # Explicitly ignore gitignore without extension
5
+ gitignore
6
+
7
+ # Except for handlebars files
8
+ !*.hbs
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ["custom"],
4
+ };
@@ -0,0 +1,9 @@
1
+ "use client";
2
+
3
+ import type { Data } from "@measured/puck";
4
+ import { Render } from "@measured/puck";
5
+ import config from "../../puck.config";
6
+
7
+ export function Client({ data }: { data: Data }) {
8
+ return <Render config={config} data={data} />;
9
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * This file implements a catch-all route that renders the user-facing pages
3
+ * generated by Puck. For any route visited (with exception of other hardcoded
4
+ * pages in /app), it will check your database (via `getPage`) for a Puck page
5
+ * and render it using <Render>.
6
+ *
7
+ * All routes produced by this page are statically rendered using incremental
8
+ * static site generation. After the first visit, the page will be cached as
9
+ * a static file. Subsequent visits will receive the cache. Publishing a page
10
+ * will invalidate the cache as the page is written in /api/puck/route.ts
11
+ */
12
+
13
+ import { Client } from "./client";
14
+ import { notFound } from "next/navigation";
15
+ import { Metadata } from "next";
16
+ import { getPage } from "../../lib/get-page";
17
+
18
+ export async function generateMetadata({
19
+ params: { puckPath = [] },
20
+ }: {
21
+ params: { puckPath: string[] };
22
+ }): Promise<Metadata> {
23
+ const path = `/${puckPath.join("/")}`;
24
+
25
+ return {
26
+ title: getPage(path)?.root.title,
27
+ };
28
+ }
29
+
30
+ export default async function Page({
31
+ params: { puckPath = [] },
32
+ }: {
33
+ params: { puckPath: string[] };
34
+ }) {
35
+ const path = `/${puckPath.join("/")}`;
36
+ const data = getPage(path);
37
+
38
+ if (!data) {
39
+ return notFound();
40
+ }
41
+
42
+ return <Client data={data} />;
43
+ }
44
+
45
+ // Force Next.js to produce static pages: https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#dynamic
46
+ // Delete this if you need dynamic rendering, such as access to headers or cookies
47
+ export const dynamic = "force-static";
@@ -0,0 +1,14 @@
1
+ import "@measured/puck/dist/index.css";
2
+ import "./styles.css";
3
+
4
+ export default function RootLayout({
5
+ children,
6
+ }: {
7
+ children: React.ReactNode;
8
+ }) {
9
+ return (
10
+ <html lang="en">
11
+ <body>{children}</body>
12
+ </html>
13
+ );
14
+ }
@@ -0,0 +1 @@
1
+ export { default, generateMetadata } from "./[...puckPath]/page";
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import type { Data } from "@measured/puck";
4
+ import { Puck } from "@measured/puck";
5
+ import config from "../../../puck.config";
6
+
7
+ export function Client({ path, data }: { path: string; data: Data }) {
8
+ return (
9
+ <Puck
10
+ config={config}
11
+ data={data}
12
+ onPublish={async (data: Data) => {
13
+ await fetch("/puck/api", {
14
+ method: "post",
15
+ body: JSON.stringify({ data, path }),
16
+ });
17
+ }}
18
+ />
19
+ );
20
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * This file implements a *magic* catch-all route that renders the Puck editor.
3
+ *
4
+ * This route exposes /puck/[...puckPath], but is disabled by middleware.ts. The middleware
5
+ * then rewrites all URL requests ending in `/edit` to this route, allowing you to visit any
6
+ * page in your application and add /edit to the end to spin up a Puck editor.
7
+ *
8
+ * This approach enables public pages to be statically rendered whilst the /puck route can
9
+ * remain dynamic.
10
+ *
11
+ * NB this route is public, and you will need to add authentication
12
+ */
13
+
14
+ import { Client } from "./client";
15
+ import { Metadata } from "next";
16
+ import { getPage } from "../../../lib/get-page";
17
+
18
+ export async function generateMetadata({
19
+ params: { puckPath = [] },
20
+ }: {
21
+ params: { puckPath: string[] };
22
+ }): Promise<Metadata> {
23
+ const path = `/${puckPath.join("/")}`;
24
+
25
+ return {
26
+ title: "Puck: " + path,
27
+ };
28
+ }
29
+
30
+ export default async function Page({
31
+ params: { puckPath = [] },
32
+ }: {
33
+ params: { puckPath: string[] };
34
+ }) {
35
+ const path = `/${puckPath.join("/")}`;
36
+ const data = getPage(path);
37
+
38
+ return <Client path={path} data={data} />;
39
+ }
@@ -0,0 +1,25 @@
1
+ import { revalidatePath } from "next/cache";
2
+ import { NextResponse } from "next/server";
3
+ import fs from "fs";
4
+
5
+ export async function POST(request: Request) {
6
+ const payload = await request.json();
7
+
8
+ const existingData = JSON.parse(
9
+ fs.existsSync("database.json")
10
+ ? fs.readFileSync("database.json", "utf-8")
11
+ : "{}"
12
+ );
13
+
14
+ const updatedData = {
15
+ ...existingData,
16
+ [payload.path]: payload.data,
17
+ };
18
+
19
+ fs.writeFileSync("database.json", JSON.stringify(updatedData));
20
+
21
+ // Purge Next.js cache
22
+ revalidatePath(payload.path);
23
+
24
+ return NextResponse.json({ status: "ok" });
25
+ }
@@ -0,0 +1 @@
1
+ export { default, generateMetadata } from "./[...puckPath]/page";
@@ -0,0 +1,5 @@
1
+ html,
2
+ body {
3
+ margin: 0;
4
+ padding: 0;
5
+ }
@@ -0,0 +1,36 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # next.js
12
+ /.next/
13
+ /out/
14
+
15
+ # production
16
+ /build
17
+
18
+ # misc
19
+ .DS_Store
20
+ *.pem
21
+
22
+ # debug
23
+ npm-debug.log*
24
+ yarn-debug.log*
25
+ yarn-error.log*
26
+
27
+ # local env files
28
+ .env.local
29
+ .env.development.local
30
+ .env.test.local
31
+ .env.production.local
32
+
33
+ # vercel
34
+ .vercel
35
+
36
+ database.json
@@ -0,0 +1,11 @@
1
+ import { Data } from "@measured/puck";
2
+ import fs from "fs";
3
+
4
+ // Replace with call to your database
5
+ export const getPage = (path: string) => {
6
+ const allData: Record<string, Data> | null = fs.existsSync("database.json")
7
+ ? JSON.parse(fs.readFileSync("database.json", "utf-8"))
8
+ : null;
9
+
10
+ return allData ? allData[path] : null;
11
+ };
@@ -0,0 +1,27 @@
1
+ import { NextResponse } from "next/server";
2
+
3
+ import type { NextRequest } from "next/server";
4
+
5
+ export async function middleware(req: NextRequest) {
6
+ const res = NextResponse.next();
7
+
8
+ if (req.method === "GET") {
9
+ // Rewrite routes that match "/[...puckPath]/edit" to "/puck/[...puckPath]"
10
+ if (req.nextUrl.pathname.endsWith("/edit")) {
11
+ const pathWithoutEdit = req.nextUrl.pathname.slice(
12
+ 0,
13
+ req.nextUrl.pathname.length - 5
14
+ );
15
+ const pathWithEditPrefix = `/puck${pathWithoutEdit}`;
16
+
17
+ return NextResponse.rewrite(new URL(pathWithEditPrefix, req.url));
18
+ }
19
+
20
+ // Disable "/puck/[...puckPath]"
21
+ if (req.nextUrl.pathname.startsWith("/puck")) {
22
+ return NextResponse.redirect(new URL("/", req.url));
23
+ }
24
+ }
25
+
26
+ return res;
27
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/basic-features/typescript for more information.
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ reactStrictMode: true,
3
+ transpilePackages: ["ui"],
4
+ };
@@ -0,0 +1,25 @@
1
+ import type { Config } from "@measured/puck";
2
+
3
+ type Props = {
4
+ HeadingBlock: { title: string };
5
+ };
6
+
7
+ export const config: Config<Props> = {
8
+ components: {
9
+ HeadingBlock: {
10
+ fields: {
11
+ title: { type: "text" },
12
+ },
13
+ defaultProps: {
14
+ title: "Heading",
15
+ },
16
+ render: ({ title }) => (
17
+ <div style={{ padding: 64 }}>
18
+ <h1>{title}</h1>
19
+ </div>
20
+ ),
21
+ },
22
+ },
23
+ };
24
+
25
+ export default config;
@@ -0,0 +1,20 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Default",
4
+ "compilerOptions": {
5
+ "composite": false,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "esModuleInterop": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "inlineSources": false,
11
+ "isolatedModules": true,
12
+ "moduleResolution": "node",
13
+ "noUnusedLocals": false,
14
+ "noUnusedParameters": false,
15
+ "preserveWatchOutput": true,
16
+ "skipLibCheck": true,
17
+ "strict": true
18
+ },
19
+ "exclude": ["node_modules"]
20
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Next.js",
4
+ "extends": "./base.json",
5
+ "compilerOptions": {
6
+ "plugins": [{ "name": "next" }],
7
+ "allowJs": true,
8
+ "declaration": false,
9
+ "declarationMap": false,
10
+ "incremental": true,
11
+ "jsx": "preserve",
12
+ "lib": ["dom", "dom.iterable", "esnext"],
13
+ "module": "esnext",
14
+ "noEmit": true,
15
+ "resolveJsonModule": true,
16
+ "strict": false,
17
+ "target": "es5"
18
+ },
19
+ "include": ["src", "next-env.d.ts"],
20
+ "exclude": ["node_modules"]
21
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig/nextjs.json",
3
+ "compilerOptions": {
4
+ "plugins": [{ "name": "next" }]
5
+ },
6
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
7
+ "exclude": ["node_modules"]
8
+ }
@@ -1,72 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "fs";
4
- import path from "path";
5
- import { glob } from "glob";
6
- import { dirname } from "path";
7
- import { fileURLToPath } from "url";
8
-
9
- const verbose = false;
10
-
11
- const run = async () => {
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = dirname(__filename);
14
-
15
- // Copy template files to the new directory
16
- const recipePath = path.join(__dirname, "../../../recipes");
17
- const templatePath = path.join(__dirname, "../templates");
18
-
19
- if (!fs.existsSync(recipePath)) {
20
- console.error(`No recipe directory could be found at ${recipePath}.`);
21
- return;
22
- }
23
-
24
- if (!fs.existsSync(templatePath)) {
25
- console.error(`No template directory could be found at ${templatePath}.`);
26
- return;
27
- }
28
-
29
- // Copy recipe files
30
- const recipeFiles = glob.sync(`**/*`, {
31
- cwd: recipePath,
32
- nodir: true,
33
- dot: true,
34
- });
35
-
36
- console.warn(
37
- `⚠️ The following files use handlebars templates. Please manually update them:`
38
- );
39
-
40
- let counter = 0;
41
-
42
- for (const recipeFile of recipeFiles) {
43
- const filePath = path.join(recipePath, recipeFile);
44
-
45
- const targetPath = filePath
46
- .replace(recipePath, templatePath)
47
- .replace(".gitignore", "gitignore"); // rename .gitignore to gitignore so NPM publish doesn't ignore it
48
-
49
- // Don't copy file if it's templated by handlebars
50
- if (fs.existsSync(`${targetPath}.hbs`)) {
51
- console.warn(`- ${recipeFile}`);
52
- } else {
53
- if (verbose) {
54
- console.log(`Copying ${filePath} -> ${targetPath}`);
55
- }
56
-
57
- const data = await fs.readFileSync(filePath, "utf-8");
58
-
59
- const dir = path.dirname(targetPath);
60
-
61
- await fs.mkdirSync(dir, { recursive: true });
62
-
63
- await fs.writeFileSync(targetPath, data);
64
-
65
- counter += 1;
66
- }
67
- }
68
-
69
- console.log(`Copied ${counter} files into generator!`);
70
- };
71
-
72
- await run();