create-noxion 0.1.1 → 0.2.0

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.
Files changed (83) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +159 -5
  4. package/dist/index.js.map +1 -1
  5. package/dist/scaffold.d.ts +9 -1
  6. package/dist/scaffold.d.ts.map +1 -1
  7. package/dist/scaffold.js +20 -3
  8. package/dist/scaffold.js.map +1 -1
  9. package/dist/templates/docs/.env +8 -0
  10. package/dist/templates/docs/.env.example +9 -0
  11. package/dist/templates/docs/app/[slug]/page.tsx +49 -0
  12. package/dist/templates/docs/app/api/notion-webhook/route.ts +12 -0
  13. package/dist/templates/docs/app/api/revalidate/route.ts +12 -0
  14. package/dist/templates/docs/app/globals.css +31 -0
  15. package/dist/templates/docs/app/layout.tsx +35 -0
  16. package/dist/templates/docs/app/not-found.tsx +38 -0
  17. package/dist/templates/docs/app/page.tsx +45 -0
  18. package/dist/templates/docs/app/providers.tsx +45 -0
  19. package/dist/templates/docs/app/robots.ts +7 -0
  20. package/dist/templates/docs/app/sitemap.ts +9 -0
  21. package/dist/templates/docs/app/theme-script.tsx +16 -0
  22. package/dist/templates/docs/lib/config.ts +24 -0
  23. package/dist/templates/docs/lib/notion.ts +48 -0
  24. package/dist/templates/docs/next.config.ts +26 -0
  25. package/dist/templates/docs/noxion.config.ts +23 -0
  26. package/dist/templates/docs/package.json +25 -0
  27. package/dist/templates/docs/tsconfig.json +19 -0
  28. package/dist/templates/full/.env +10 -0
  29. package/dist/templates/full/.env.example +11 -0
  30. package/dist/templates/full/app/[slug]/page.tsx +55 -0
  31. package/dist/templates/full/app/api/notion-webhook/route.ts +12 -0
  32. package/dist/templates/full/app/api/revalidate/route.ts +12 -0
  33. package/dist/templates/full/app/globals.css +31 -0
  34. package/dist/templates/full/app/home-content.tsx +58 -0
  35. package/dist/templates/full/app/layout.tsx +35 -0
  36. package/dist/templates/full/app/not-found.tsx +38 -0
  37. package/dist/templates/full/app/page.tsx +50 -0
  38. package/dist/templates/full/app/providers.tsx +45 -0
  39. package/dist/templates/full/app/robots.ts +7 -0
  40. package/dist/templates/full/app/sitemap.ts +9 -0
  41. package/dist/templates/full/app/tag/[tag]/page.tsx +66 -0
  42. package/dist/templates/full/app/theme-script.tsx +16 -0
  43. package/dist/templates/full/lib/config.ts +24 -0
  44. package/dist/templates/full/lib/notion.ts +66 -0
  45. package/dist/templates/full/next.config.ts +26 -0
  46. package/dist/templates/full/noxion.config.ts +40 -0
  47. package/dist/templates/full/package.json +25 -0
  48. package/dist/templates/full/tsconfig.json +19 -0
  49. package/dist/templates/nextjs/.env.example +1 -0
  50. package/dist/templates/nextjs/app/api/notion-webhook/route.ts +12 -0
  51. package/dist/templates/nextjs/app/page.tsx +3 -3
  52. package/dist/templates/nextjs/app/tag/[tag]/page.tsx +4 -4
  53. package/dist/templates/nextjs/lib/config.ts +1 -0
  54. package/dist/templates/nextjs/lib/notion.ts +5 -5
  55. package/dist/templates/plugin/noxion-plugin.json +10 -0
  56. package/dist/templates/plugin/package.json +41 -0
  57. package/dist/templates/plugin/src/__tests__/plugin.test.ts +39 -0
  58. package/dist/templates/plugin/src/index.ts +20 -0
  59. package/dist/templates/plugin/tsconfig.json +20 -0
  60. package/dist/templates/portfolio/.env +8 -0
  61. package/dist/templates/portfolio/.env.example +9 -0
  62. package/dist/templates/portfolio/app/[slug]/page.tsx +48 -0
  63. package/dist/templates/portfolio/app/api/notion-webhook/route.ts +12 -0
  64. package/dist/templates/portfolio/app/api/revalidate/route.ts +12 -0
  65. package/dist/templates/portfolio/app/globals.css +31 -0
  66. package/dist/templates/portfolio/app/layout.tsx +35 -0
  67. package/dist/templates/portfolio/app/not-found.tsx +38 -0
  68. package/dist/templates/portfolio/app/page.tsx +70 -0
  69. package/dist/templates/portfolio/app/providers.tsx +45 -0
  70. package/dist/templates/portfolio/app/robots.ts +7 -0
  71. package/dist/templates/portfolio/app/sitemap.ts +9 -0
  72. package/dist/templates/portfolio/app/theme-script.tsx +16 -0
  73. package/dist/templates/portfolio/lib/config.ts +24 -0
  74. package/dist/templates/portfolio/lib/notion.ts +48 -0
  75. package/dist/templates/portfolio/next.config.ts +26 -0
  76. package/dist/templates/portfolio/noxion.config.ts +23 -0
  77. package/dist/templates/portfolio/package.json +25 -0
  78. package/dist/templates/portfolio/tsconfig.json +19 -0
  79. package/dist/templates/theme/package.json +42 -0
  80. package/dist/templates/theme/src/index.ts +40 -0
  81. package/dist/templates/theme/styles/theme.css +23 -0
  82. package/dist/templates/theme/tsconfig.json +20 -0
  83. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ import { loadConfig } from "@noxion/core";
2
+ import type { NoxionConfig } from "@noxion/core";
3
+ import noxionConfigInput from "../noxion.config";
4
+
5
+ function createConfig(): NoxionConfig {
6
+ try {
7
+ return loadConfig(noxionConfigInput);
8
+ } catch {
9
+ return {
10
+ name: noxionConfigInput.name ?? "{{SITE_NAME}}",
11
+ domain: noxionConfigInput.domain ?? "{{DOMAIN}}",
12
+ author: noxionConfigInput.author ?? "{{AUTHOR}}",
13
+ description: noxionConfigInput.description ?? "{{SITE_DESCRIPTION}}",
14
+ language: noxionConfigInput.language ?? "en",
15
+ defaultTheme: noxionConfigInput.defaultTheme ?? "system",
16
+ defaultPageType: "blog",
17
+ revalidate: noxionConfigInput.revalidate ?? 3600,
18
+ plugins: noxionConfigInput.plugins,
19
+ collections: noxionConfigInput.collections,
20
+ };
21
+ }
22
+ }
23
+
24
+ export const siteConfig = createConfig();
@@ -0,0 +1,66 @@
1
+ import { createNotionClient, fetchCollection, fetchPage, downloadImages, mapImages } from "@noxion/core";
2
+ import type { NoxionPage, BlogPage, ExtendedRecordMap } from "@noxion/core";
3
+ import { join } from "node:path";
4
+ import { siteConfig } from "./config";
5
+
6
+ const notion = createNotionClient({
7
+ authToken: process.env.NOTION_TOKEN || undefined,
8
+ });
9
+
10
+ export async function getAllPages(): Promise<NoxionPage[]> {
11
+ const collections = siteConfig.collections ?? [];
12
+ if (collections.length === 0) return [];
13
+
14
+ try {
15
+ const results = await Promise.all(
16
+ collections.map((col) => fetchCollection(notion, col))
17
+ );
18
+ return results.flat();
19
+ } catch (error) {
20
+ console.error("Failed to fetch pages:", error);
21
+ return [];
22
+ }
23
+ }
24
+
25
+ export async function getPagesByType(pageType: string): Promise<NoxionPage[]> {
26
+ const pages = await getAllPages();
27
+ return pages.filter((p) => p.pageType === pageType);
28
+ }
29
+
30
+ export async function getPageBySlug(slug: string): Promise<NoxionPage | undefined> {
31
+ const pages = await getAllPages();
32
+ return pages.find((p) => p.slug === slug);
33
+ }
34
+
35
+ export async function getPageRecordMap(pageId: string): Promise<ExtendedRecordMap> {
36
+ const recordMap = await fetchPage(notion, pageId);
37
+
38
+ if (process.env.NODE_ENV === "production") {
39
+ try {
40
+ const outputDir = join(process.cwd(), "public");
41
+ const urlMap = await downloadImages(recordMap, outputDir, { concurrency: 5 });
42
+ const localUrlMap: Record<string, string> = {};
43
+ for (const [originalUrl, localPath] of Object.entries(urlMap)) {
44
+ localUrlMap[originalUrl] = `/images/${localPath.split("/images/").pop()}`;
45
+ }
46
+ return mapImages(recordMap, localUrlMap);
47
+ } catch (error) {
48
+ console.error("Image download failed, using original URLs:", error);
49
+ }
50
+ }
51
+
52
+ return recordMap;
53
+ }
54
+
55
+ export function getAllTags(posts: NoxionPage[]): string[] {
56
+ const tagSet = new Set<string>();
57
+ for (const post of posts) {
58
+ const tags = post.metadata.tags;
59
+ if (Array.isArray(tags)) {
60
+ for (const tag of tags) {
61
+ tagSet.add(tag as string);
62
+ }
63
+ }
64
+ }
65
+ return [...tagSet].sort();
66
+ }
@@ -0,0 +1,26 @@
1
+ import type { NextConfig } from "next";
2
+
3
+ const nextConfig: NextConfig = {
4
+ output: "standalone",
5
+ transpilePackages: [
6
+ "@noxion/core",
7
+ "@noxion/renderer",
8
+ "@noxion/adapter-nextjs",
9
+ "@noxion/notion-renderer",
10
+ "notion-client",
11
+ "notion-types",
12
+ "notion-utils",
13
+ ],
14
+ images: {
15
+ remotePatterns: [
16
+ { protocol: "https", hostname: "www.notion.so" },
17
+ { protocol: "https", hostname: "notion.so" },
18
+ { protocol: "https", hostname: "images.unsplash.com" },
19
+ { protocol: "https", hostname: "s3.us-west-2.amazonaws.com" },
20
+ { protocol: "https", hostname: "prod-files-secure.s3.us-west-2.amazonaws.com" },
21
+ ],
22
+ },
23
+ staticPageGenerationTimeout: 300,
24
+ };
25
+
26
+ export default nextConfig;
@@ -0,0 +1,40 @@
1
+ import { defineConfig, createRSSPlugin } from "@noxion/core";
2
+
3
+ const config = defineConfig({
4
+ name: process.env.SITE_NAME ?? "{{SITE_NAME}}",
5
+ domain: process.env.SITE_DOMAIN ?? "{{DOMAIN}}",
6
+ author: process.env.SITE_AUTHOR ?? "{{AUTHOR}}",
7
+ description: process.env.SITE_DESCRIPTION ?? "{{SITE_DESCRIPTION}}",
8
+ language: "en",
9
+ defaultTheme: "system",
10
+ defaultPageType: "blog",
11
+ revalidate: 3600,
12
+ revalidateSecret: process.env.REVALIDATE_SECRET,
13
+ collections: [
14
+ {
15
+ name: "blog",
16
+ databaseId: process.env.NOTION_PAGE_ID!,
17
+ pageType: "blog",
18
+ },
19
+ {
20
+ name: "docs",
21
+ databaseId: process.env.DOCS_NOTION_ID || process.env.NOTION_PAGE_ID!,
22
+ pageType: "docs",
23
+ pathPrefix: "/docs",
24
+ },
25
+ {
26
+ name: "portfolio",
27
+ databaseId: process.env.PORTFOLIO_NOTION_ID || process.env.NOTION_PAGE_ID!,
28
+ pageType: "portfolio",
29
+ pathPrefix: "/projects",
30
+ },
31
+ ],
32
+ plugins: [
33
+ createRSSPlugin({
34
+ feedPath: "/feed.xml",
35
+ limit: 20,
36
+ }),
37
+ ],
38
+ });
39
+
40
+ export default config;
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start"
9
+ },
10
+ "dependencies": {
11
+ "@noxion/core": "^0.0.2",
12
+ "@noxion/renderer": "^0.0.2",
13
+ "@noxion/adapter-nextjs": "^0.0.2",
14
+ "next": "^16.1.6",
15
+ "react": "^19.1.0",
16
+ "react-dom": "^19.1.0",
17
+ "notion-client": "^7.8.2"
18
+ },
19
+ "devDependencies": {
20
+ "@types/node": "^22.0.0",
21
+ "@types/react": "^19.2.14",
22
+ "@types/react-dom": "^19.2.3",
23
+ "typescript": "^5.7.0"
24
+ }
25
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "jsx": "react-jsx",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "plugins": [{ "name": "next" }],
15
+ "outDir": "./dist"
16
+ },
17
+ "include": ["**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
18
+ "exclude": ["node_modules", ".next", "out", "dist"]
19
+ }
@@ -6,3 +6,4 @@ SITE_DOMAIN={{DOMAIN}}
6
6
  SITE_AUTHOR={{AUTHOR}}
7
7
  SITE_DESCRIPTION={{SITE_DESCRIPTION}}
8
8
  REVALIDATE_SECRET=
9
+ NOTION_WEBHOOK_SECRET=
@@ -0,0 +1,12 @@
1
+ import { revalidatePath } from "next/cache";
2
+ import { createNotionWebhookHandler } from "@noxion/adapter-nextjs";
3
+ import { siteConfig } from "../../../lib/config";
4
+
5
+ const handler = createNotionWebhookHandler({
6
+ config: siteConfig,
7
+ revalidatePath,
8
+ });
9
+
10
+ export async function POST(request: Request) {
11
+ return handler(request);
12
+ }
@@ -10,10 +10,10 @@ export default async function HomePage() {
10
10
  const postCards = posts.map((post) => ({
11
11
  title: post.title,
12
12
  slug: post.slug,
13
- date: post.date,
14
- tags: post.tags,
13
+ date: post.metadata.date,
14
+ tags: post.metadata.tags,
15
15
  coverImage: post.coverImage,
16
- category: post.category,
16
+ category: post.metadata.category,
17
17
  }));
18
18
 
19
19
  return <HomeContent posts={postCards} allTags={allTags} />;
@@ -35,14 +35,14 @@ export default async function TagPage({
35
35
  const allTags = getAllTags(posts);
36
36
 
37
37
  const filteredPosts = posts
38
- .filter((p) => p.tags.includes(decodedTag))
38
+ .filter((p) => p.metadata.tags.includes(decodedTag))
39
39
  .map((post) => ({
40
40
  title: post.title,
41
41
  slug: post.slug,
42
- date: post.date,
43
- tags: post.tags,
42
+ date: post.metadata.date,
43
+ tags: post.metadata.tags,
44
44
  coverImage: post.coverImage,
45
- category: post.category,
45
+ category: post.metadata.category,
46
46
  }));
47
47
 
48
48
  return (
@@ -14,6 +14,7 @@ function createConfig(): NoxionConfig {
14
14
  description: noxionConfigInput.description ?? "{{SITE_DESCRIPTION}}",
15
15
  language: noxionConfigInput.language ?? "en",
16
16
  defaultTheme: noxionConfigInput.defaultTheme ?? "system",
17
+ defaultPageType: "blog",
17
18
  revalidate: noxionConfigInput.revalidate ?? 3600,
18
19
  plugins: noxionConfigInput.plugins,
19
20
  };
@@ -1,5 +1,5 @@
1
1
  import { createNotionClient, fetchBlogPosts, fetchPage, fetchPostBySlug, downloadImages, mapImages } from "@noxion/core";
2
- import type { BlogPost, ExtendedRecordMap } from "@noxion/core";
2
+ import type { BlogPage, ExtendedRecordMap } from "@noxion/core";
3
3
  import { join } from "node:path";
4
4
  import { siteConfig } from "./config";
5
5
 
@@ -7,7 +7,7 @@ const notion = createNotionClient({
7
7
  authToken: process.env.NOTION_TOKEN || undefined,
8
8
  });
9
9
 
10
- export async function getAllPosts(): Promise<BlogPost[]> {
10
+ export async function getAllPosts(): Promise<BlogPage[]> {
11
11
  if (!siteConfig.rootNotionPageId) return [];
12
12
  try {
13
13
  return await fetchBlogPosts(notion, siteConfig.rootNotionPageId);
@@ -17,7 +17,7 @@ export async function getAllPosts(): Promise<BlogPost[]> {
17
17
  }
18
18
  }
19
19
 
20
- export async function getPostBySlug(slug: string): Promise<BlogPost | undefined> {
20
+ export async function getPostBySlug(slug: string): Promise<BlogPage | undefined> {
21
21
  if (!siteConfig.rootNotionPageId) return undefined;
22
22
  try {
23
23
  return await fetchPostBySlug(notion, siteConfig.rootNotionPageId, slug);
@@ -47,10 +47,10 @@ export async function getPageRecordMap(pageId: string): Promise<ExtendedRecordMa
47
47
  return recordMap;
48
48
  }
49
49
 
50
- export function getAllTags(posts: BlogPost[]): string[] {
50
+ export function getAllTags(posts: BlogPage[]): string[] {
51
51
  const tagSet = new Set<string>();
52
52
  for (const post of posts) {
53
- for (const tag of post.tags) {
53
+ for (const tag of post.metadata.tags) {
54
54
  tagSet.add(tag);
55
55
  }
56
56
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "noxion-plugin-{{PLUGIN_NAME}}",
3
+ "description": "{{PLUGIN_DESCRIPTION}}",
4
+ "version": "0.1.0",
5
+ "noxion": ">=0.2.0",
6
+ "hooks": ["transformPosts"],
7
+ "pageTypes": [],
8
+ "hasConfig": false,
9
+ "keywords": ["noxion-plugin"]
10
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "noxion-plugin-{{PLUGIN_NAME}}",
3
+ "version": "0.1.0",
4
+ "description": "{{PLUGIN_DESCRIPTION}}",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "noxion-plugin.json",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "sideEffects": false,
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "test": "bun test",
25
+ "prepublishOnly": "bun run build"
26
+ },
27
+ "keywords": [
28
+ "notion",
29
+ "noxion",
30
+ "noxion-plugin"
31
+ ],
32
+ "peerDependencies": {
33
+ "@noxion/core": ">=0.2.0"
34
+ },
35
+ "devDependencies": {
36
+ "@noxion/core": "^0.2.0",
37
+ "@noxion/plugin-utils": "^0.1.0",
38
+ "@types/bun": "^1.2.0",
39
+ "typescript": "^5.7.0"
40
+ }
41
+ }
@@ -0,0 +1,39 @@
1
+ import { describe, it, expect, beforeEach } from "bun:test";
2
+ import {
3
+ createMockBlogPages,
4
+ createTestPlugin,
5
+ resetMockCounter,
6
+ } from "@noxion/plugin-utils";
7
+ import { createPlugin } from "../index";
8
+
9
+ describe("noxion-plugin-{{PLUGIN_NAME}}", () => {
10
+ beforeEach(() => {
11
+ resetMockCounter();
12
+ });
13
+
14
+ it("has the correct name", () => {
15
+ const plugin = createPlugin({});
16
+ expect(plugin.name).toBe("noxion-plugin-{{PLUGIN_NAME}}");
17
+ });
18
+
19
+ it("passes posts through when enabled", () => {
20
+ const plugin = createPlugin({ enabled: true });
21
+ const posts = createMockBlogPages(3);
22
+ const result = plugin.transformPosts!({ posts });
23
+ expect(result).toHaveLength(3);
24
+ });
25
+
26
+ it("passes posts through when disabled", () => {
27
+ const plugin = createPlugin({ enabled: false });
28
+ const posts = createMockBlogPages(3);
29
+ const result = plugin.transformPosts!({ posts });
30
+ expect(result).toHaveLength(3);
31
+ });
32
+
33
+ it("works as a test plugin", () => {
34
+ const plugin = createTestPlugin({
35
+ name: "noxion-plugin-{{PLUGIN_NAME}}",
36
+ });
37
+ expect(plugin.name).toBe("noxion-plugin-{{PLUGIN_NAME}}");
38
+ });
39
+ });
@@ -0,0 +1,20 @@
1
+ import type { NoxionPlugin, PluginFactory } from "@noxion/core";
2
+
3
+ export interface PluginOptions {
4
+ enabled?: boolean;
5
+ }
6
+
7
+ export const createPlugin: PluginFactory<PluginOptions> = (options = {}) => {
8
+ const plugin: NoxionPlugin = {
9
+ name: "noxion-plugin-{{PLUGIN_NAME}}",
10
+
11
+ transformPosts({ posts }) {
12
+ if (options.enabled === false) return posts;
13
+ return posts;
14
+ },
15
+ };
16
+
17
+ return plugin;
18
+ };
19
+
20
+ export default createPlugin;
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "sourceMap": true,
9
+ "outDir": "./dist",
10
+ "rootDir": "./src",
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "skipLibCheck": true,
14
+ "forceConsistentCasingInFileNames": true,
15
+ "isolatedModules": true,
16
+ "types": ["bun-types"]
17
+ },
18
+ "include": ["src/**/*.ts"],
19
+ "exclude": ["node_modules", "dist", "src/**/*.test.ts", "src/**/__tests__/**"]
20
+ }
@@ -0,0 +1,8 @@
1
+ NOTION_PAGE_ID={{NOTION_PAGE_ID}}
2
+ NOTION_TOKEN=
3
+ NOTION_SPACE_ID=
4
+ SITE_NAME={{SITE_NAME}}
5
+ SITE_DOMAIN={{DOMAIN}}
6
+ SITE_AUTHOR={{AUTHOR}}
7
+ SITE_DESCRIPTION={{SITE_DESCRIPTION}}
8
+ REVALIDATE_SECRET=
@@ -0,0 +1,9 @@
1
+ NOTION_PAGE_ID={{NOTION_PAGE_ID}}
2
+ NOTION_TOKEN=
3
+ NOTION_SPACE_ID=
4
+ SITE_NAME={{SITE_NAME}}
5
+ SITE_DOMAIN={{DOMAIN}}
6
+ SITE_AUTHOR={{AUTHOR}}
7
+ SITE_DESCRIPTION={{SITE_DESCRIPTION}}
8
+ REVALIDATE_SECRET=
9
+ NOTION_WEBHOOK_SECRET=
@@ -0,0 +1,48 @@
1
+ import { notFound } from "next/navigation";
2
+ import type { Metadata } from "next";
3
+ import { NotionPage } from "@noxion/renderer";
4
+ import { generateNoxionMetadata } from "@noxion/adapter-nextjs";
5
+ import { getProjectBySlug, getPageRecordMap, getAllProjects } from "../../lib/notion";
6
+ import { siteConfig } from "../../lib/config";
7
+
8
+ export const revalidate = 3600;
9
+
10
+ export async function generateStaticParams() {
11
+ const projects = await getAllProjects();
12
+ return projects.map((p) => ({ slug: p.slug }));
13
+ }
14
+
15
+ export async function generateMetadata({
16
+ params,
17
+ }: {
18
+ params: Promise<{ slug: string }>;
19
+ }): Promise<Metadata> {
20
+ const { slug } = await params;
21
+ const project = await getProjectBySlug(slug);
22
+ if (!project) return { title: "Not Found" };
23
+ return generateNoxionMetadata(project, siteConfig);
24
+ }
25
+
26
+ export default async function ProjectPage({
27
+ params,
28
+ }: {
29
+ params: Promise<{ slug: string }>;
30
+ }) {
31
+ const { slug } = await params;
32
+ const project = await getProjectBySlug(slug);
33
+ if (!project) notFound();
34
+
35
+ const recordMap = await getPageRecordMap(project.id);
36
+
37
+ return (
38
+ <article>
39
+ <NotionPage
40
+ recordMap={recordMap}
41
+ rootPageId={project.id}
42
+ fullPage
43
+ previewImages
44
+ mapPageUrl={(pageId: string) => `/${pageId}`}
45
+ />
46
+ </article>
47
+ );
48
+ }
@@ -0,0 +1,12 @@
1
+ import { revalidatePath } from "next/cache";
2
+ import { createNotionWebhookHandler } from "@noxion/adapter-nextjs";
3
+ import { siteConfig } from "../../../lib/config";
4
+
5
+ const handler = createNotionWebhookHandler({
6
+ config: siteConfig,
7
+ revalidatePath,
8
+ });
9
+
10
+ export async function POST(request: Request) {
11
+ return handler(request);
12
+ }
@@ -0,0 +1,12 @@
1
+ import { revalidatePath } from "next/cache";
2
+ import { createRevalidateHandler } from "@noxion/adapter-nextjs";
3
+ import { siteConfig } from "../../../lib/config";
4
+
5
+ const handler = createRevalidateHandler({
6
+ config: siteConfig,
7
+ revalidatePath,
8
+ });
9
+
10
+ export async function POST(request: Request) {
11
+ return handler(request as never);
12
+ }
@@ -0,0 +1,31 @@
1
+ @import "@noxion/notion-renderer/styles";
2
+
3
+ *,
4
+ *::before,
5
+ *::after {
6
+ box-sizing: border-box;
7
+ margin: 0;
8
+ padding: 0;
9
+ }
10
+
11
+ html {
12
+ font-family: var(--noxion-font-sans, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);
13
+ -webkit-font-smoothing: antialiased;
14
+ -moz-osx-font-smoothing: grayscale;
15
+ }
16
+
17
+ body {
18
+ background-color: var(--noxion-background, #fff);
19
+ color: var(--noxion-foreground, #0a0a0a);
20
+ min-height: 100vh;
21
+ }
22
+
23
+ [data-theme="dark"] body {
24
+ background-color: var(--noxion-background, #0a0a0a);
25
+ color: var(--noxion-foreground, #fafafa);
26
+ }
27
+
28
+ a {
29
+ color: inherit;
30
+ text-decoration: none;
31
+ }
@@ -0,0 +1,35 @@
1
+ import type { Metadata } from "next";
2
+ import { generateNoxionListMetadata, generateWebSiteLD } from "@noxion/adapter-nextjs";
3
+ import { siteConfig } from "../lib/config";
4
+ import { ThemeScript } from "./theme-script";
5
+ import { Providers } from "./providers";
6
+ import "./globals.css";
7
+
8
+ export function generateMetadata(): Metadata {
9
+ return generateNoxionListMetadata(siteConfig);
10
+ }
11
+
12
+ export default function RootLayout({
13
+ children,
14
+ }: {
15
+ children: React.ReactNode;
16
+ }) {
17
+ const jsonLd = generateWebSiteLD(siteConfig);
18
+
19
+ return (
20
+ <html lang={siteConfig.language} suppressHydrationWarning>
21
+ <head>
22
+ <ThemeScript />
23
+ <script
24
+ type="application/ld+json"
25
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
26
+ />
27
+ </head>
28
+ <body>
29
+ <Providers siteName={siteConfig.name} author={siteConfig.author}>
30
+ {children}
31
+ </Providers>
32
+ </body>
33
+ </html>
34
+ );
35
+ }
@@ -0,0 +1,38 @@
1
+ import Link from "next/link";
2
+
3
+ export default function NotFound() {
4
+ return (
5
+ <div
6
+ style={{
7
+ display: "flex",
8
+ flexDirection: "column",
9
+ alignItems: "center",
10
+ justifyContent: "center",
11
+ minHeight: "50vh",
12
+ textAlign: "center",
13
+ gap: "1rem",
14
+ }}
15
+ >
16
+ <h1 style={{ fontSize: "4rem", fontWeight: 700, color: "var(--noxion-mutedForeground, #737373)" }}>
17
+ 404
18
+ </h1>
19
+ <p style={{ fontSize: "1.125rem", color: "var(--noxion-mutedForeground, #737373)" }}>
20
+ This page could not be found.
21
+ </p>
22
+ <Link
23
+ href="/"
24
+ style={{
25
+ marginTop: "1rem",
26
+ padding: "0.5rem 1.5rem",
27
+ borderRadius: "var(--noxion-border-radius, 0.5rem)",
28
+ backgroundColor: "var(--noxion-primary, #2563eb)",
29
+ color: "var(--noxion-primaryForeground, #fff)",
30
+ fontSize: "0.875rem",
31
+ fontWeight: 500,
32
+ }}
33
+ >
34
+ Go Home
35
+ </Link>
36
+ </div>
37
+ );
38
+ }