create-puck-app 0.11.1-canary.1a8f6b8 → 0.11.1
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.
- package/package.json +8 -2
- package/templates/gitignore +8 -0
- package/templates/next/.eslintrc.js +4 -0
- package/templates/next/app/[...puckPath]/client.tsx +9 -0
- package/templates/next/app/[...puckPath]/page.tsx +47 -0
- package/templates/next/app/layout.tsx +14 -0
- package/templates/next/app/page.tsx +1 -0
- package/templates/next/app/puck/[...puckPath]/client.tsx +20 -0
- package/templates/next/app/puck/[...puckPath]/page.tsx +39 -0
- package/templates/next/app/puck/api/route.ts +25 -0
- package/templates/next/app/puck/page.tsx +1 -0
- package/templates/next/app/styles.css +5 -0
- package/templates/next/gitignore +36 -0
- package/templates/next/lib/get-page.ts +11 -0
- package/templates/next/middleware.ts +27 -0
- package/templates/next/next-env.d.ts +5 -0
- package/templates/next/next.config.js +4 -0
- package/templates/next/puck.config.tsx +25 -0
- package/templates/next/tsconfig/base.json +20 -0
- package/templates/next/tsconfig/nextjs.json +21 -0
- package/templates/next/tsconfig.json +8 -0
- package/scripts/generate.js +0 -72
package/package.json
CHANGED
@@ -1,15 +1,21 @@
|
|
1
1
|
{
|
2
2
|
"name": "create-puck-app",
|
3
|
-
"version": "0.11.1
|
3
|
+
"version": "0.11.1",
|
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,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 @@
|
|
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,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,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
|
+
}
|
package/scripts/generate.js
DELETED
@@ -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();
|